Skip to content
This repository has been archived by the owner on Feb 15, 2022. It is now read-only.

Support for Kraken API #9

Closed
grigio opened this issue Jul 31, 2016 · 41 comments
Closed

Support for Kraken API #9

grigio opened this issue Jul 31, 2016 · 41 comments

Comments

@grigio
Copy link
Contributor

grigio commented Jul 31, 2016

I think this is the part that needs to integrated in zenbot
https://www.kraken.com/help/api#private-user-trading

Is this part implemented on zenbot 3.x for some other exchange?

@carlos8f
Copy link
Contributor

not yet, but soon :)

This was referenced Aug 16, 2016
@grigio
Copy link
Contributor Author

grigio commented Aug 19, 2016

If you are brave enough.. here is the kraken porting of default_logic.js which use gdax as data source. There are still many hardcoded stuff, but it quite works for now.

I still get errors like Error: Kraken API returned error: Order:Insufficient funds sometimes

var first_run = true
var last_balance_sig
var sync_start_balance = false

module.exports = function container (get, set, clear) {
  var c = get('config')
  var o = get('utils.object_get')
  var n = require('numbro')
  var tb = require('timebucket')
  var sig = require('sig')
  var format_currency = get('utils.format_currency')
  var get_timestamp = get('utils.get_timestamp')
  // var CoinbaseExchange = require('coinbase-exchange')
  var KrakenClient = require('kraken-api');

  // Put here your credentials
  const kraken_key = ''
  const kraken_secret = ''

  var client
  function onOrder (err, order) {
    if (err) return get('logger').error('order err', err, order, {feed: 'errors'})
    // if (resp.statusCode !== 200) {
    //   console.error(order)
    //   return get('logger').error('non-200 status: ' + resp.statusCode, {data: {statusCode: resp.statusCode, body: order}})
    // }
    get('logger').info('kraken', ('order-id: ' + order.txid[0]).cyan, {data: {order: order}})
    function getStatus () {

      client.api('QueryOrders', {"txid": order.txid[0] }, function(err, data) {
        if (err) return get('logger').error('getOrder err', err)

        const order_confirmation = data[order.txid[0]]

          if (order.status === 'closed') {
            return get('logger').info('kraken', ('order ' + order.txid[0] + ' closed ').cyan, {data: {order: order}})
          }
          else {
            get('logger').info('kraken', ('order ' + order.txid[0] + ' ' + order.status).cyan, {data: {order: order}})
            setTimeout(getStatus, 5000)
          }

      });

      // client.getOrder(order.id, function (err, resp, order) {
      //   if (err) return get('logger').error('getOrder err', err)
      //   if (resp.statusCode !== 200) {
      //     console.error(order)
      //     return get('logger').error('non-200 status from getOrder: ' + resp.statusCode, {data: {statusCode: resp.statusCode, body: order}})
      //   }
      //   if (order.status === 'done') {
      //     return get('logger').info('kraken', ('order ' + order.id + ' done: ' + order.done_reason).cyan, {data: {order: order}})
      //   }
      //   else {
      //     get('logger').info('kraken', ('order ' + order.id + ' ' + order.status).cyan, {data: {order: order}})
      //     setTimeout(getStatus, 5000)
      //   }
      // })

    }
    getStatus()
  }
  return [
    // BEGIN DEFAULT TRADE LOGIC
    // default params
    function (tick, trigger, rs, cb) {
      //get('logger').info('trader', 'KRAKEN strategy loaded')
      rs.asset = 'BTC'
      rs.currency = 'USD'
      rs.rsi_period = '1h'
      rs.rsi_up = 63
      rs.rsi_down = 47
      rs.check_period = '1m'
      rs.exchange = 'gdax'
      rs.selector = 'data.trades.' + rs.exchange + '.' + rs.asset + '-' + rs.currency
      rs.hold_ticks = 100 // hold x check_period after trade
      rs.trade_pct = 0.95 // trade % of current balance
      rs.min_trade = 0.01
      rs.start_balance = 1000
      rs.min_roi_delta = -0.06 // accept a loss of up to 6%
      cb()
    },
    // sync balance if key is present and we're in the `run` command
    function (tick, trigger, rs, cb) {
      if (get('command') !== 'run' || !c.gdax_key) {
        return cb()
      }
      if (!client) {
        // client = new CoinbaseExchange.AuthenticatedClient(c.gdax_key, c.gdax_secret, c.gdax_passphrase)
        client = new KrakenClient(kraken_key, kraken_secret)
      }

      client.api('Balance', null, function(err, data) {
        if (err) {
          get('logger').info('kraken err', err)
        }

        // HACK non 200
        if(!data) {
          return cb()
        }
          // if (resp.statusCode !== 200) {
          //   console.error(data)
          //   get('logger').error('non-200 status from exchange: ' + resp.statusCode, {data: {statusCode: resp.statusCode, body: accounts}})
          //   return cb()
          // }

          rs.balance = {}

          var accounts = data.result
          var gdax_accounts = [
            {currency:'BTC', balance:accounts['XXBT']},
            {currency:'USD', balance:accounts['ZUSD']}
          ]

          //get('logger').info('acc', JSON.stringify(gdax_accounts))

          gdax_accounts.forEach(function (account) {
            if (account.currency === rs.currency) {
              rs.balance[rs.currency] = n(account.balance).value()
            }
            else if (account.currency === rs.asset) {
              rs.balance[rs.asset] = n(account.balance).value()
            }
          })

          if (first_run) {
            sync_start_balance = true
          }
          var balance_sig = sig(rs.balance)
          if (balance_sig !== last_balance_sig) {
            get('logger').info('kraken', 'balance'.grey, n(rs.balance[rs.asset]).format('0.000').white, rs.asset.grey, n(rs.balance[rs.currency]).format('0.00').yellow, rs.currency.grey, {feed: 'exchange'})
            last_balance_sig = balance_sig
          }
          cb()
      })

      // client.getAccounts(function (err, resp, accounts) {
      //   if (err) throw err
      //   if (resp.statusCode !== 200) {
      //     console.error(accounts)
      //     get('logger').error('non-200 status from exchange: ' + resp.statusCode, {data: {statusCode: resp.statusCode, body: accounts}})
      //     return cb()
      //   }
      //   rs.balance = {}
      //   accounts.forEach(function (account) {
      //     if (account.currency === rs.currency) {
      //       rs.balance[rs.currency] = n(account.balance).value()
      //     }
      //     else if (account.currency === rs.asset) {
      //       rs.balance[rs.asset] = n(account.balance).value()
      //     }
      //   })
      //   if (first_run) {
      //     sync_start_balance = true
      //   }
      //   var balance_sig = sig(rs.balance)
      //   if (balance_sig !== last_balance_sig) {
      //     get('logger').info(rs.exchange, 'balance'.grey, n(rs.balance[rs.asset]).format('0.000').white, rs.asset.grey, n(rs.balance[rs.currency]).format('0.00').yellow, rs.currency.grey, {feed: 'exchange'})
      //     last_balance_sig = balance_sig
      //   }
      //   cb()
      // })
    },
    function (tick, trigger, rs, cb) {
      // note the last close price
      rs.market_price = o(tick, rs.selector + '.close')
      rs.ticks || (rs.ticks = 0)
      rs.progress || (rs.progress = 0)
      if (!rs.market_price) return cb()
      if (!rs.balance) {
        // start with start_balance, neutral position
        rs.balance = {}
        rs.balance[rs.currency] = n(rs.start_balance).divide(2).value()
        rs.balance[rs.asset] = n(rs.start_balance).divide(2).divide(rs.market_price).value()
      }
      rs.ticks++
      if (tick.size !== rs.check_period) {
        return cb()
      }
      // check price diff
      rs.close = o(tick || {}, rs.selector + '.close')
      // get rsi
      rs.rsi_tick_id = tb(tick.time).resize(rs.rsi_period).toString()
      get('ticks').load(get('app_name') + ':' + rs.rsi_tick_id, function (err, rsi_tick) {
        if (err) return cb(err)
        var rsi = o(rsi_tick || {}, rs.selector + '.rsi')
        var trend
        if (rsi) {
          rs.rsi = rsi
        }
        // require minimum data
        rs.close || (rs.close = o(rsi_tick || {}, rs.selector + '.close'))
        if (!rs.rsi) {
          get('logger').info('trader', ('no ' + rs.rsi_period + ' RSI for tick ' + rs.rsi_tick_id).red, {feed: 'trader'})
        }
        else if (rs.rsi.samples < c.rsi_periods) {
          get('logger').info('trader', (rs.rsi_period + ' RSI: not enough samples for tick ' + rs.rsi_tick_id + ': ' + rs.rsi.samples).red, {feed: 'trader'})
        }
        else if (!rs.close) {
          get('logger').info('trader', ('no close price for tick ' + rs.rsi_tick_id).red, {feed: 'trader'})
        }
        else {
          if (rs.rsi.value >= rs.rsi_up) {
            trend = 'UP'
          }
          else if (rs.rsi.value <= rs.rsi_down) {
            trend = 'DOWN'
          }
          else {
            trend = null
          }
        }
        if (trend !== rs.trend) {
          get('logger').info('trader', 'RSI:'.grey + rs.rsi.ansi, ('trend: ' + rs.trend + ' -> ' + trend).yellow, {feed: 'trader'})
          delete rs.balance_warning
          delete rs.roi_warning
        }
        rs.trend = trend
        cb()
      })
    },
    // @todo MACD
    function (tick, trigger, rs, cb) {
      cb()
    },
    // trigger trade signals
    function (tick, trigger, rs, cb) {
      if (rs.trend && rs.balance && rs.market_price) {
        var size, new_balance = {}
        // delay buying or selling, perhaps the trend intensifies
        if (rs.hold_ticks_active) {
          rs.hold_ticks_active--
        }
        if (rs.hold_ticks_active) {
          rs.progress = n(1).subtract(n(rs.hold_ticks_active).divide(rs.hold_ticks)).value()
          return cb()
        }
        rs.progress = 1
        if (rs.trend === 'DOWN') {
          // calculate sell size
          size = rs.balance[rs.asset]
        }
        else if (rs.trend === 'UP') {
          // calculate buy size
          size = n(rs.balance[rs.currency]).divide(rs.market_price).value()
        }
        size = n(size || 0).multiply(rs.trade_pct).value()
        // min size
        if (!size || size < rs.min_trade) {
          if (!rs.balance_warning) {
            get('logger').info('trader', 'trend: '.grey, rs.trend, ('not enough balance, aborting trade!').red, {feed: 'trader'})
          }
          rs.balance_warning = true
          return cb()
        }
        if (rs.trend === 'DOWN') {
          // SELL!
          new_balance[rs.currency] = n(rs.balance[rs.currency]).add(n(size).multiply(rs.market_price)).value()
          new_balance[rs.asset] = n(rs.balance[rs.asset]).subtract(size).value()
          rs.op = 'sell'
        }
        else if (rs.trend === 'UP') {
          // BUY!
          new_balance[rs.asset] = n(rs.balance[rs.asset]).add(size).value()
          new_balance[rs.currency] = n(rs.balance[rs.currency]).subtract(n(size).multiply(rs.market_price)).value()
          rs.op = 'buy'
        }
        else {
          // unknown trend
          get('logger').info('trader', ('unkown trend (' + rs.trend + ') aborting trade!').red, {feed: 'trader'})
          return cb()
        }
        // consolidate balance
        rs.new_end_balance = n(new_balance[rs.currency]).add(n(new_balance[rs.asset]).multiply(rs.market_price)).value()
        if (sync_start_balance) {
          rs.start_balance = rs.new_end_balance
          sync_start_balance = false
        }
        rs.new_roi = n(rs.new_end_balance).divide(rs.start_balance).value()
        rs.new_roi_delta = n(rs.new_roi).subtract(rs.roi || 0).value()
        if (rs.roi && rs.new_roi_delta < rs.min_roi_delta) {
          if (!rs.roi_warning) {
            get('logger').info('trader', ('new ROI below delta threshold (' + n(rs.new_roi_delta).format('%0.000') + ' < ' + n(rs.min_roi_delta).format('%0.000') + ') aborting ' + rs.op + '!').red, {feed: 'trader'})
          }
          rs.roi_warning = true
          return cb()
        }
        rs.hold_ticks_active = rs.hold_ticks + 1
        rs.balance = new_balance
        rs.end_balance = rs.new_end_balance
        rs.roi = rs.new_roi
        rs.trades || (rs.trades = 0)
        rs.trades++
        var trade = {
          type: rs.op,
          asset: rs.asset,
          currency: rs.currency,
          exchange: rs.exchange,
          price: rs.market_price,
          market: true,
          size: size,
          rsi: rs.rsi.value,
          roi: rs.roi
        }
        trigger(trade)
        if (get('command') === 'run' && c.gdax_key) {
          // var params = {
          //   type: 'market',
          //   size: n(size).format('0.000000'),
          //   product_id: rs.asset + '-' + rs.currency
          // }
          // client[rs.op](params, function (err, resp, order) {
          //   onOrder(err, resp, order)
          // })

          const order = {
            pair: 'XXBTZUSD',
            type: rs.op,
            ordertype: 'market', // market
            //price: 0.01, //optional (in second currency)
            volume: size //(first value)
          }

          client.api('AddOrder', order, function(err, data) {
              if(err) {
                console.log(err)
              }
              else {
                console.log(data.result)
                onOrder(err, data.result)
              }
          })

        }
      }
      cb()
    },
    function (tick, trigger, rs, cb) {
      first_run = false
      cb()
    }
    // END DEFAULT TRADE LOGIC
  ]
}

@grigio
Copy link
Contributor Author

grigio commented Aug 19, 2016

Currently the code below works but I'm having weird errors:

08/19/2016 09:20:01 PM CEST [    reporter]    5m4905447      22 trades. 1h RSI 69x14 CLOSE:   $575.21 USD trend:UP [link]
08/19/2016 09:20:01 PM CEST [      trader] trend:  UP not enough balance, aborting trade! [link]
08/19/2016 09:20:01 PM CEST [kraken balance] balance XXX BTC XXX USD [link]
08/19/2016 09:19:42 PM CEST [     reducer]    m24527239       5 trades. 08/19/2016 09:19:31 PM CEST  BUY         4.097 at       $575.33 BTC/USD [link]
08/19/2016 09:19:35 PM CEST [        gdax]    m24527239       4 trades. 08/19/2016 09:19:31 PM CEST SELL         0.385 at       $575.32 BTC/USD [link]
08/19/2016 09:19:03 PM CEST [      runner] starting [link]
08/19/2016 09:19:03 PM CEST [    launcher] cmd `run` booting [link]
08/19/2016 09:19:02 PM CEST [    launcher] cmd `run` exited with code 1, respawning now. [link]
08/19/2016 09:19:02 PM CEST [      kraken] order-id: XXX-XXX-XXX [link]
08/19/2016 09:19:01 PM CEST [      action] m24527238 buy {...}
08/19/2016 09:18:42 PM CEST [     reducer]    m24527238  

In particular the trade part i get [ trader] trend: UP not enough balance, aborting trade! but the order at market price and the balance is correctly updated.. @carlos8f ideas?

@carlos8f
Copy link
Contributor

the run command crashed right after the buy action, which wasn't printed in the web console, but you should get a stack trace in the stderr log. The subsequent trend-up abort message is because the run_state for run command wasn't saved (due to the crash), and it forgot that it just traded, so it tried to trade again. If the run_state is persistent as it should be, there would be a hold period (controlled by hold_ticks setting) between trades.

@grigio
Copy link
Contributor Author

grigio commented Aug 19, 2016

Thanks, yes in the stderr the error was more explicit. I had an undefined where I check the order status.

Can I add kraken-api in the package.json? or you have another way to support the dependencies in the plugins?

@carlos8f
Copy link
Contributor

ahh, cool. adding kraken-api is fine.

carlos8f pushed a commit that referenced this issue Aug 19, 2016
@backenders-game
Copy link

I am running this, but i got a lot [ kraken err] {} a lot.
I tried running it ./run.sh &>zenbot.log for stderr, but the output is same as in console.

How can i fix it and do i need to? Or maybe there is newer script for kraken.

@grigio
Copy link
Contributor Author

grigio commented Aug 24, 2016

@Sorrow2 yes it changed a bit, i'll share an updated version

@grigio
Copy link
Contributor Author

grigio commented Aug 24, 2016

Here is the code, you also have to use the kraken pairs convention in your configs ex.

c.default_selector = "kraken.XXBT-ZUSD"

var first_run = true
var last_balance_sig
var sync_start_balance = false
var assert = require('assert')
var n = require('numbro')
var tb = require('timebucket')
var sig = require('sig')
// var CoinbaseExchange = require('coinbase-exchange')

var KrakenClient = require('kraken-api');

// Put here your credentials
const kraken_key = ''
const kraken_secret = ''


module.exports = function container (get, set, clear) {
  var c = get('config')
  var o = get('utils.object_get')
  var format_currency = get('utils.format_currency')
  var get_timestamp = get('utils.get_timestamp')
  var get_duration = get('utils.get_duration')
  var get_tick_str = get('utils.get_tick_str')
  var options = get('options')
  var client
  var start = new Date().getTime()
  function onOrder (err, order) {
    // if (err) return get('logger').error('order err', err, resp, order, {feed: 'errors'})
    // if (resp.statusCode !== 200) {
    //   console.error(order)
    //   return get('logger').error('non-200 status: ' + resp.statusCode, {data: {statusCode: resp.statusCode, body: order}})
    // }
    // get('logger').info('gdax', c.default_selector.grey, ('order-id: ' + order.id).cyan, {data: {order: order}})
    get('logger').info('kraken', ('order-id: ' + order.txid[0]).cyan, {data: {order: order}})

    function getStatus () {

      client.api('QueryOrders', {"txid": order.txid[0] }, function(err, data) {
        // fix {} is ok
        //if (err) return get('logger').error('getOrder err', err)

        const order_confirmation = data.result[order.txid[0]]

          if (order_confirmation.status === 'closed') {
            return get('logger').info('kraken', ('order ' + order.txid[0] + ' closed ').cyan, {data: {order: order_confirmation}})
          }
          else {
            get('logger').info('kraken', ('order ' + order.txid[0] + ' ' + order_confirmation.status).cyan, {data: {order: order_confirmation}})
            setTimeout(getStatus, 5000)
          }

      })

      // client.getOrder(order.id, function (err, resp, order) {
      //   if (err) return get('logger').error('getOrder err', err)
      //   if (resp.statusCode !== 200) {
      //     console.error(order)
      //     return get('logger').error('non-200 status from getOrder: ' + resp.statusCode, {data: {statusCode: resp.statusCode, body: order}})
      //   }
      //   if (order.status === 'done') {
      //     return get('logger').info('gdax', c.default_selector.grey, ('order ' + order.id + ' done: ' + order.done_reason).cyan, {data: {order: order}})
      //   }
      //   else {
      //     get('logger').info('gdax', c.default_selector.grey, ('order ' + order.id + ' ' + order.status).cyan, {data: {order: order}})
      //     setTimeout(getStatus, 5000)
      //   }
      // })
    }
    getStatus()
  }
  return [
    // BEGIN DEFAULT TRADE LOGIC
    // default params
    function (tick, trigger, rs, cb) {
      rs.agent = USER_AGENT
      var sMatch = c.default_selector.match(/^([^\.]+)\.([^-]+)-([^-]+)$/)
      assert(sMatch)
      rs.exchange = sMatch[1]
      rs.asset = sMatch[2]
      rs.currency = sMatch[3]
      if (options.verbose && get('command') === 'run') {
        get('logger').info('trader', c.default_selector.grey, get_tick_str(tick.id), 'running logic'.grey, rs.asset.grey, rs.currency.grey, {feed: 'trader'})
      }
      rs.rsi_period = '1h'
      rs.rsi_up = 69 //70
      rs.rsi_down = 29 //30
      rs.check_period = '1m' //'5m'
      rs.selector = 'data.trades.' + c.default_selector
      rs.trade_pct = 0.98 // trade % of current balance
      rs.fee_pct = 0.0026 // apply 0.25% taker fee
      var products = get('exchanges.' + rs.exchange).products
      products.forEach(function (product) {
        if (product.asset === rs.asset && product.currency === rs.currency) {
          rs.product = product
        }
      })
      if (!rs.product) return cb(new Error('no product for ' + c.default_selector))
      rs.min_trade = n(rs.product.min_size).multiply(1).value()
      rs.sim_start_balance = 1000
      rs.min_buy_wait = 86400000 * 1 // wait in ms after action before buying
      rs.min_sell_wait = 86400000 * 1 // wait in ms after action before selling
      rs.min_performance = -0.4 // abort trades with lower performance score
      cb()
    },
    // sync balance if key is present and we're in the `run` command
    function (tick, trigger, rs, cb) {
      if (get('command') !== 'run' || !c.gdax_key) {
        rs.start_balance = rs.sim_start_balance
        // add timestamp for simulations
        if (c.reporter_cols.indexOf('timestamp') === -1) {
          c.reporter_cols.unshift('timestamp')
          if (get('command') === 'run') {
            get('logger').info('trader', c.default_selector.grey, ('No trader API key provided. Starting in advisor mode. --Zen').yellow, {feed: 'trader'})
          }
        }
        if (get('command') === 'sim') {
          // change reporting interval for sims
          c.reporter_sizes = ['1h']
        }
        return cb()
      }
      if (!client) {
        client = new KrakenClient(kraken_key, kraken_secret)
        // client = new CoinbaseExchange.AuthenticatedClient(c.gdax_key, c.gdax_secret, c.gdax_passphrase)
      }

      client.api('Balance', null, function(err, data) {
        // not {}
        // if (err) {
        //   get('logger').info('kraken err bal', JSON.stringify(err))
        // }

        // HACK non 200
        if(!data) {
          return cb()
        }
          // if (resp.statusCode !== 200) {
          //   console.error(data)
          //   get('logger').error('non-200 status from exchange: ' + resp.statusCode, {data: {statusCode: resp.statusCode, body: accounts}})
          //   return cb()
          // }

          rs.balance = {}

          var accounts = data.result
/*
          var gdax_accounts = [
            {currency:'XXBT', balance:accounts['XXBT']},
            {currency:'ZUSD', balance:accounts['ZUSD']}
          ]
*/
          var gdax_accounts = []
          Object.keys(accounts).map( key => gdax_accounts.push({currency: key, balance:accounts[key]}) )

          //get('logger').info('acc', JSON.stringify(gdax_accounts))

          gdax_accounts.forEach(function (account) {
            if (account.currency === rs.currency) {
              rs.balance[rs.currency] = n(account.balance).value()
            }
            else if (account.currency === rs.asset) {
              rs.balance[rs.asset] = n(account.balance).value()
            }
          })

          if (first_run) {
            sync_start_balance = true
          }
          var balance_sig = sig(rs.balance)
          if (balance_sig !== last_balance_sig) {
            get('logger').info('kraken balance', 'balance'.grey, n(rs.balance[rs.asset]).format('0.000').white, rs.asset.grey, n(rs.balance[rs.currency]).format('0.00').yellow, rs.currency.grey, {feed: 'exchange'})
            last_balance_sig = balance_sig
          }
          cb()
      })

      // client.getAccounts(function (err, resp, accounts) {
      //   if (err) throw err
      //   if (resp.statusCode !== 200) {
      //     console.error(accounts)
      //     get('logger').error('non-200 status from exchange: ' + resp.statusCode, {data: {statusCode: resp.statusCode, body: accounts}})
      //     return cb()
      //   }
      //   rs.balance = {}
      //   accounts.forEach(function (account) {
      //     if (account.currency === rs.currency) {
      //       rs.balance[rs.currency] = n(account.balance).value()
      //     }
      //     else if (account.currency === rs.asset) {
      //       rs.balance[rs.asset] = n(account.balance).value()
      //     }
      //   })
      //   if (first_run) {
      //     sync_start_balance = true
      //   }
      //   var balance_sig = sig(rs.balance)
      //   if (balance_sig !== last_balance_sig) {
      //     get('logger').info('trader', c.default_selector.grey, '"Starting REAL trading! Hold on to your butts!" --Zen'.cyan, ' Balance:'.grey, n(rs.balance[rs.asset]).format('0.000').white, rs.asset.grey, n(rs.balance[rs.currency]).format('0.00').yellow, rs.currency.grey, {feed: 'exchange'})
      //     last_balance_sig = balance_sig
      //   }
      //   cb()
      // })
    },
    function (tick, trigger, rs, cb) {
      if (tick.size !== rs.check_period) {
        return cb()
      }
      // note the last close price
      var market_price = o(tick, rs.selector + '.close')
      // sometimes the tick won't have a close price for this selector.
      // keep old close price in memory.
      if (market_price) {
        rs.market_price = market_price
      }
      rs.ticks || (rs.ticks = 0)
      rs.progress || (rs.progress = 0)
      if (!rs.market_price) {
        //get('logger').info('trader', ('no close price for tick ' + tick.id).red, {feed: 'trader'})
        return cb()
      }
      if (!rs.balance) {
        // start with start_balance, neutral position
        rs.balance = {}
        rs.balance[rs.currency] = n(rs.start_balance).divide(2).value()
        rs.balance[rs.asset] = n(rs.start_balance).divide(2).divide(rs.market_price).value()
      }
      rs.consolidated_balance = n(rs.balance[rs.currency]).add(n(rs.balance[rs.asset]).multiply(rs.market_price)).value()
      if (sync_start_balance) {
        rs.start_balance = rs.consolidated_balance
        sync_start_balance = false
      }
      rs.roi = n(rs.consolidated_balance).divide(rs.start_balance).value()
      rs.ticks++
      // get rsi
      rs.rsi_tick_id = tb(tick.time).resize(rs.rsi_period).toString()
      get('ticks').load(get('app_name') + ':' + rs.rsi_tick_id, function (err, rsi_tick) {
        if (err) return cb(err)
        var rsi = o(rsi_tick || {}, rs.selector + '.rsi')
        var trend
        if (rsi) {
          rs.rsi = rsi
        }
        // require minimum data
        if (!rs.rsi) {
          if (!rs.rsi_warning) {
            get('logger').info('trader', c.default_selector.grey, ('no ' + rs.rsi_period + ' RSI for tick ' + rs.rsi_tick_id).red, {feed: 'trader'})
          }
          rs.rsi_warning = true
        }
        else if (rs.rsi.samples < c.rsi_periods) {
          if (!rs.rsi_warning) {
            get('logger').info('trader', c.default_selector.grey, (rs.rsi_period + ' RSI: not enough samples for tick ' + rs.rsi_tick_id + ': ' + rs.rsi.samples).red, {feed: 'trader'})
          }
          rs.rsi_warning = true
        }
        else {
          if (rs.rsi.value >= rs.rsi_up) {
            trend = 'UP'
          }
          else if (rs.rsi.value <= rs.rsi_down) {
            trend = 'DOWN'
          }
          else {
            trend = null
          }
        }
        if (trend !== rs.trend) {
          get('logger').info('trader', c.default_selector.grey, 'RSI:'.grey + rs.rsi.ansi, ('trend: ' + rs.trend + ' -> ' + trend).yellow, {feed: 'trader'})
          delete rs.balance_warning
          delete rs.roi_warning
          delete rs.rsi_warning
          delete rs.delta_warning
          delete rs.buy_warning
          delete rs.perf_warning
          delete rs.action_warning
          delete rs.trend_warning
        }
        rs.trend = trend
        cb()
      })
    },
    // @todo MACD
    function (tick, trigger, rs, cb) {
      cb()
    },
    // trigger trade signals
    function (tick, trigger, rs, cb) {
      if (tick.size !== rs.check_period) {
        return cb()
      }
      // for run command, don't trade unless this is a new tick
      if (get('command') !== 'sim' && tick.time < start) {
        get('logger').info('trader', c.default_selector.grey, ('skipping historical tick ' + tick.id).grey, {feed: 'trader'})
        return cb()
      }
      if (rs.trend && !rs.trend_warning) {
        get('logger').info('trader', c.default_selector.grey, ('acting on trend: ' + rs.trend + '!').yellow, {feed: 'trader'})
        if (!rs.balance) {
          get('logger').info('trader', c.default_selector.grey, ('no balance to act on trend: ' + rs.trend + '!').red, {feed: 'trader'})
        }
        if (!rs.market_price) {
          get('logger').info('trader', c.default_selector.grey, ('no market_price to act on trend: ' + rs.trend + '!').red, {feed: 'trader'})
        }
        rs.trend_warning = true
      }
      rs.progress = 1
      if (rs.trend && rs.balance && rs.market_price) {
        var size, new_balance = {}
        if (rs.trend === 'DOWN') {
          // calculate sell size
          size = rs.balance[rs.asset]
        }
        else if (rs.trend === 'UP') {
          // calculate buy size
          size = n(rs.balance[rs.currency]).divide(rs.market_price).value()
        }
        size = n(size || 0).multiply(rs.trade_pct).value()
        if (rs.trend === 'DOWN') {
          // SELL!
          if (rs.last_action_time && tick.time - rs.last_action_time <= rs.min_sell_wait) {
            if (!rs.sell_warning) {
              get('logger').info('trader', c.default_selector.grey, ('too soon to sell after ' + rs.last_op + '! waiting ' + get_duration(n(rs.min_sell_wait).subtract(n(tick.time).subtract(rs.last_action_time)).multiply(1000).value())).red, {feed: 'trader'})
            }
            rs.sell_warning = true
            return cb()
          }
          new_balance[rs.currency] = n(rs.balance[rs.currency]).add(n(size).multiply(rs.market_price)).value()
          new_balance[rs.asset] = n(rs.balance[rs.asset]).subtract(size).value()
          rs.op = 'sell'
          if (!rs.action_warning) {
            get('logger').info('trader', c.default_selector.grey, ('attempting to sell ' + n(size).format('0.00000000') + ' ' + rs.asset + ' for ' + format_currency(n(size).multiply(rs.market_price).value(), rs.currency) + ' ' + rs.currency).yellow, {feed: 'trader'})
          }
          rs.action_warning = true
        }
        else if (rs.trend === 'UP') {
          // BUY!
          if (rs.last_action_time && tick.time - rs.last_action_time <= rs.min_buy_wait) {
            if (!rs.buy_warning) {
              get('logger').info('trader', c.default_selector.grey, ('too soon to buy after ' + rs.last_op + '! waiting ' + get_duration(n(rs.min_buy_wait).subtract(n(tick.time).subtract(rs.last_action_time)).multiply(1000).value())).red, {feed: 'trader'})
            }
            rs.buy_warning = true
            return cb()
          }
          new_balance[rs.asset] = n(rs.balance[rs.asset]).add(size).value()
          new_balance[rs.currency] = n(rs.balance[rs.currency]).subtract(n(size).multiply(rs.market_price)).value()
          rs.op = 'buy'
          if (!rs.action_warning) {
            get('logger').info('trader', c.default_selector.grey, ('attempting to buy ' + n(size).format('0.00000000') + ' ' + rs.asset + ' for ' + format_currency(n(size).multiply(rs.market_price).value(), rs.currency) + ' ' + rs.currency).yellow, {feed: 'trader'})
          }
          rs.action_warning = true
        }
        else {
          // unknown trend
          get('logger').info('trader', c.default_selector.grey, ('unkown trend (' + rs.trend + ') aborting trade!').red, {feed: 'trader'})
          return cb()
        }
        // min size
        if (!size || size < rs.min_trade) {
          if (!rs.balance_warning) {
            get('logger').info('trader', c.default_selector.grey, 'trend: '.grey, rs.trend, ('not enough funds (' + (rs.op === 'sell' ? n(size).format('0.00000000') : format_currency(rs.balance[rs.currency], rs.currency)) + ' ' + (rs.op === 'sell' ? rs.asset : rs.currency) + ') to execute min. ' + rs.op + ' ' + rs.min_trade + ', aborting trade!').red, {feed: 'trader'})
          }
          rs.balance_warning = true
          return cb()
        }
        // fee calc
        rs.fee = n(size).multiply(rs.market_price).multiply(rs.fee_pct).value()
        new_balance[rs.currency] = n(new_balance[rs.currency]).subtract(rs.fee).value()
        // consolidate balance
        rs.new_end_balance = n(new_balance[rs.currency]).add(n(new_balance[rs.asset]).multiply(rs.market_price)).value()
        rs.new_roi = n(rs.new_end_balance).divide(rs.start_balance).value()
        rs.new_roi_delta = n(rs.new_roi).subtract(rs.roi || 0).value()

        if (rs.op === 'buy') {
          // % drop
          rs.performance = rs.last_sell_price ? n(rs.last_sell_price).subtract(rs.market_price).divide(rs.last_sell_price).value() : null
          rs.waited = rs.last_action_time ? get_duration(n(tick.time).subtract(rs.last_action_time).multiply(1000).value()) : null
        }
        else {
          // % gain
          rs.performance = rs.last_buy_price ? n(rs.market_price).subtract(rs.last_buy_price).divide(rs.last_buy_price).value() : null
          rs.waited = rs.last_action_time ? get_duration(n(tick.time).subtract(rs.last_action_time).multiply(1000).value()) : null
        }
        if (rs.min_performance && rs.performance !== null && rs.performance < rs.min_performance) {
          if (!rs.perf_warning) {
            get('logger').info('trader', c.default_selector.grey, ('aborting ' + rs.op + ' due to low perf. = ' + n(rs.performance).format('0.000')).red, {feed: 'trader'})
          }
          rs.perf_warning = true
          return cb()
        }
        rs.balance = new_balance
        rs.end_balance = rs.new_end_balance
        rs.roi = rs.new_roi
        rs.num_trades || (rs.num_trades = 0)
        rs.num_trades++
        var trade = {
          type: rs.op,
          asset: rs.asset,
          currency: rs.currency,
          exchange: rs.exchange,
          price: rs.market_price,
          fee: rs.fee,
          market: true,
          size: size,
          rsi: rs.rsi.value,
          roi: rs.roi,
          roi_delta: rs.new_roi_delta,
          performance: rs.performance,
          waited: rs.waited,
          balance: new_balance,
          end_balance: rs.new_end_balance
        }
        trigger(trade)
        if (client) {

          const order = {
            // pair: 'XXBTZUSD',
            pair: rs.asset+''+rs.currency,
            type: rs.op,
            ordertype: 'market', // market
            //price: 0.01, //optional (in second currency)
            volume: size //(first value)
          }

          client.api('AddOrder', order, function(err, data) {
              if(err) {
                console.log(err)
              }
              else {
                console.log(data.result)
                onOrder(err, data.result)
              }
          })

          // var params = {
          //   type: 'market',
          //   size: n(size).format('0.000000'),
          //   product_id: rs.asset + '-' + rs.currency
          // }
          // client[rs.op](params, function (err, resp, order) {
          //   onOrder(err, resp, order)
          // })
        }
        else if (!rs.sim_warning) {
          get('logger').info('trader', c.default_selector.grey, ('Relax! This is a simulated trade! No real transaction will take place. --Zen').yellow, {feed: 'trader'})
          rs.sim_warning = true
        }
        if (rs.op === 'buy') {
          rs.last_buy_price = rs.market_price
        }
        else {
          rs.last_sell_price = rs.market_price
        }
        rs.last_action_time = tick.time
        rs.last_op = rs.op
      }
      cb()
    },
    function (tick, trigger, rs, cb) {
      first_run = false
      cb()
    }
    // END DEFAULT TRADE LOGIC
  ]
}

@backenders-game
Copy link

backenders-game commented Aug 26, 2016

Thanks.
I don't get kraken err {} now, but i see no RSI change now (longer logs are the same). I am running with 3.5.14 build.
I have kraken and gdax in c.watch_exchanges. If i leave just kraken - no output in console.
Did i miss something?


08/26/2016 06:04:07 PM MSK [    reporter]    m24537063      13 trades.        ---           RSI: 38 CLOSE:      576.990 ZUSD kraken.XXBT-ZUSD BAL:      581.269 ROI:1.000
08/26/2016 06:04:19 PM MSK [        gdax]    m24537064       3 trades. 08/26/2016 06:04:05 PM MSK  BUY         3.407 at       $578.42 BTC/USD
08/26/2016 06:05:04 PM MSK [        gdax]    m24537065       2 trades. 08/26/2016 06:05:01 PM MSK  BUY         0.826 at       $578.41 BTC/USD
08/26/2016 06:05:06 PM MSK [    reporter]    m24537064       1 trades.        ---           RSI: 38 CLOSE:      576.990 ZUSD kraken.XXBT-ZUSD BAL:      581.269 ROI:1.000
08/26/2016 06:06:04 PM MSK [    reporter]    m24537065       1 trades.        ---           RSI: 38 CLOSE:      576.990 ZUSD kraken.XXBT-ZUSD BAL:      581.269 ROI:1.000
08/26/2016 06:07:09 PM MSK [    reporter]    m24537066       1 trades.        ---           RSI: 38 CLOSE:      576.990 ZUSD kraken.XXBT-ZUSD BAL:      581.269 ROI:1.000
08/26/2016 06:08:05 PM MSK [        gdax]    m24537068       2 trades. 08/26/2016 06:08:00 PM MSK  BUY         0.965 at       $578.41 BTC/USD
08/26/2016 06:08:08 PM MSK [    reporter]    m24537067       1 trades.        ---           RSI: 38 CLOSE:      576.990 ZUSD kraken.XXBT-ZUSD BAL:      581.269 ROI:1.000

@grigio
Copy link
Contributor Author

grigio commented Aug 26, 2016

Are you sure you are running the newer version because you shouldn't see [ gdax] anymore

@backenders-game
Copy link

backenders-game commented Aug 26, 2016

Yes. I run second version of script you posted here. I saved it as kraken_logic.js.

I see gdax only if i have it in c.watch_exchanges.
If i have only kraken, nothing happens

08/26/2016 07:53:45 PM MSK [    launcher] cmd `server` booting
08/26/2016 07:53:45 PM MSK [      server] zenbot/3.5.14 booted!
08/26/2016 07:53:45 PM MSK [      server] open http://localhost:3013/?secret=d14a02121fd65e to see a live graph.

No output after this.

Here is the rest of my config

// watch these exchanges
c.watch_exchanges = [
  //"bitfinex",
  // "gdax",
  "kraken",
  //"poloniex"
]

// selector for indicators, trading, etc
c.default_selector = "kraken.XXBT-ZUSD"

// add selectors in the format "{exchange-slug}.{asset}-{currency}" to graph them
c.graph_selectors = [
  c.default_selector,
  "gdax.ETH-BTC",
  "gdax.ETH-USD"
]

// trade logic
c.logic = require('./kraken_logic')

and i put c.default_selector = "kraken.XXBT-ZUSD" in config_btc_usd.js

@grigio
Copy link
Contributor Author

grigio commented Aug 26, 2016

I don't know, I replaced kraken.XXBT-ZUSD everywhere and it works. Try with --verbose

08/26/2016 08:04:01 PM CEST [      trader] kraken.XXBT-ZUSD m24537243 running logic XXBT ZUSD [link]
08/26/2016 08:04:01 PM CEST [      runner] m24537243 running [link]
08/26/2016 08:03:47 PM CEST [      kraken]    m24537243       5 trades. 08/26/2016 08:03:32 PM CEST SELL         0.601 at       578.946 XXBT/ZUSD [link]
08/26/2016 08:03:39 PM CEST [      kraken]    m24537243       3 trades. 08/26/2016 08:03:32 PM CEST SELL         0.405 at       578.943 XXBT/ZUSD [link]
08/26/2016 08:03:28 PM CEST [      kraken]    m24537243       5 trades. 08/26/2016 08:03:28 PM CEST SELL         0.517 at       578.950 XXBT/ZUSD [link]
08/26/2016 08:03:26 PM CEST [      kraken]    m24537243       2 trades. 08/26/2016 08:03:25 PM CEST SELL         0.217 at       578.950 XXBT/ZUSD [link]
08/26/2016 08:03:06 PM CEST [      runner] waiting for next 1m tick... [link]

@backenders-game
Copy link

backenders-game commented Aug 29, 2016

are you using 3.5.14?

it seems that it doesn't connect to kraken here. If i use default config and i add kraken in c.watch_exchanges, i see no kraken output.
But it works for poloniex, gdax and bitfinex.
Any idea?

@backenders-game
Copy link

I have made a fresh install of latest (3.5.16) version to VPS, and i see same issue there:
no kraken output,
but poloniex, gdax, bitfinex - fine

@grigio
Copy link
Contributor Author

grigio commented Aug 30, 2016

I don't know what to say.. usually when I get no output is because I've wrong currencies pairs.. in kraken there are no BTC or USD, you have to use XETH XXBT ZUSD ZEUR .. in every config

@backenders-game
Copy link

backenders-game commented Aug 30, 2016

i don't even add pairs.
I did fresh install. cp config_samle.js config.js
And uncomment exchanges:

// watch these exchanges
c.watch_exchanges = [
  "bitfinex",
  "gdax",
  "kraken",
  "poloniex"
]

And it works for other exchanges

08/30/2016 11:06:50 AM MSK [      server] zenbot/3.5.16 booted!
08/30/2016 11:06:50 AM MSK [      server] open http://localhost:3013/?secret=9ba935d84df1c2cc to see a live graph.
08/30/2016 11:06:50 AM MSK [    poloniex]    m24542406      17 trades. 08/30/2016 11:06:13 AM MSK  BUY         0.131 at       580.448 BTC/USDT
08/30/2016 11:06:50 AM MSK [    bitfinex]    m24542405      10 trades. 08/30/2016 11:05:52 AM MSK SELL         4.114 at       $580.00 BTC/USD
08/30/2016 11:06:50 AM MSK [        gdax]    m24542406      39 trades. 08/30/2016 11:06:46 AM MSK SELL         6.451 at       $575.68 BTC/USD
08/30/2016 11:07:00 AM MSK [      runner] m24542406 running
08/30/2016 11:07:00 AM MSK [      trader] gdax.BTC-USD m24542406 running logic BTC USD

@Labseb
Copy link

Labseb commented Aug 31, 2016

Hello,
Same issue that Sorrow2.
fresh install, i use your "kraken_logic.js", and no output from kraken.
I used the --verbose but nothing to see.

Are your sure that c.default_selector = "kraken.XXBT-ZUSD" ?
in plugins/kraken/exchange.json it's "XXBTZUD" so it could be: c.default_selector = "kraken.XXBTZUSD"
i tried it too, but nothing to see.....

@grigio
Copy link
Contributor Author

grigio commented Aug 31, 2016

this is the config.js i use:

var c = module.exports = require('./config_defaults')()

// mongo stuff
c.mongo_url = "mongodb://localhost:27017/zenbrain-multi-kraken" // change if your mongo server isn't local
c.mongo_username = null // normally not needed
c.mongo_password = null

c.gdax_key = '' // TO ENABLE BOT TRADING: set this to GDAX api key,
c.gdax_secret = '' // set this to GDAX api secret,
c.gdax_passphrase = '' // set this to GDAX api passphrase.
c.trade_log = true // log new trades as they come in.

// watch these exchanges
c.watch_exchanges = [
  //"bitfinex",
  // "gdax",
  "kraken",
  //"poloniex"
]

// selector for indicators, trading, etc
c.default_selector = "kraken.XXBT-ZUSD"


// add selectors in the format "{exchange-slug}.{asset}-{currency}" to graph them
c.graph_selectors = [
  c.default_selector,
  "kraken.XETH-XXBT",
  "kraken.XETH-ZUSD"
]

// trade logic
c.logic = require('./default_logic')

@Labseb
Copy link

Labseb commented Aug 31, 2016

ok, and your "default_logic" ?

@grigio
Copy link
Contributor Author

grigio commented Aug 31, 2016

default_logic.js

var first_run = true
var last_balance_sig
var sync_start_balance = false
var assert = require('assert')
var n = require('numbro')
var tb = require('timebucket')
var sig = require('sig')
// var CoinbaseExchange = require('coinbase-exchange')

var KrakenClient = require('kraken-api');

// Put here your credentials
const kraken_key = ''
const kraken_secret = ''


module.exports = function container (get, set, clear) {
  var c = get('config')
  var o = get('utils.object_get')
  var format_currency = get('utils.format_currency')
  var get_timestamp = get('utils.get_timestamp')
  var get_duration = get('utils.get_duration')
  var get_tick_str = get('utils.get_tick_str')
  var options = get('options')
  var client
  var start = new Date().getTime()
  function onOrder (err, order) {
    // if (err) return get('logger').error('order err', err, resp, order, {feed: 'errors'})
    // if (resp.statusCode !== 200) {
    //   console.error(order)
    //   return get('logger').error('non-200 status: ' + resp.statusCode, {data: {statusCode: resp.statusCode, body: order}})
    // }
    // get('logger').info('gdax', c.default_selector.grey, ('order-id: ' + order.id).cyan, {data: {order: order}})
    get('logger').info('kraken', ('order-id: ' + order.txid[0]).cyan, {data: {order: order}})

    function getStatus () {

      client.api('QueryOrders', {"txid": order.txid[0] }, function(err, data) {
        // fix {} is ok
        //if (err) return get('logger').error('getOrder err', err)

        const order_confirmation = data.result[order.txid[0]]

          if (order_confirmation.status === 'closed') {
            return get('logger').info('kraken', ('order ' + order.txid[0] + ' closed ').cyan, {data: {order: order_confirmation}})
          }
          else {
            get('logger').info('kraken', ('order ' + order.txid[0] + ' ' + order_confirmation.status).cyan, {data: {order: order_confirmation}})
            setTimeout(getStatus, 5000)
          }

      })

      // client.getOrder(order.id, function (err, resp, order) {
      //   if (err) return get('logger').error('getOrder err', err)
      //   if (resp.statusCode !== 200) {
      //     console.error(order)
      //     return get('logger').error('non-200 status from getOrder: ' + resp.statusCode, {data: {statusCode: resp.statusCode, body: order}})
      //   }
      //   if (order.status === 'done') {
      //     return get('logger').info('gdax', c.default_selector.grey, ('order ' + order.id + ' done: ' + order.done_reason).cyan, {data: {order: order}})
      //   }
      //   else {
      //     get('logger').info('gdax', c.default_selector.grey, ('order ' + order.id + ' ' + order.status).cyan, {data: {order: order}})
      //     setTimeout(getStatus, 5000)
      //   }
      // })
    }
    getStatus()
  }
  return [
    // BEGIN DEFAULT TRADE LOGIC
    // default params
    function (tick, trigger, rs, cb) {
      rs.agent = USER_AGENT
      var sMatch = c.default_selector.match(/^([^\.]+)\.([^-]+)-([^-]+)$/)
      assert(sMatch)
      rs.exchange = sMatch[1]
      rs.asset = sMatch[2]
      rs.currency = sMatch[3]
      if (options.verbose && get('command') === 'run') {
        get('logger').info('trader', c.default_selector.grey, get_tick_str(tick.id), 'running logic'.grey, rs.asset.grey, rs.currency.grey, {feed: 'trader'})
      }
      rs.rsi_query_limit = 100 // RSI initial value lookback
      rs.rsi_periods = 26 // RSI smoothing factor
      rs.rsi_period = '5m' // RSI tick size
      rs.rsi_up = 65 // upper RSI threshold
      rs.rsi_down = 28 // lower RSI threshold
      rs.check_period = '1m' // speed to trigger actions at
      rs.selector = 'data.trades.' + c.default_selector
      rs.trade_pct = 0.98 // trade % of current balance
      rs.fee_pct = 0.0026 // apply 0.25% taker fee
      var products = get('exchanges.' + rs.exchange).products
      products.forEach(function (product) {
        if (product.asset === rs.asset && product.currency === rs.currency) {
          rs.product = product
        }
      })
      if (!rs.product) return cb(new Error('no product for ' + c.default_selector))
      rs.min_trade = n(rs.product.min_size).multiply(1).value()
      rs.sim_start_balance = 10000
      rs.min_double_wait = 86400000 * 1 // wait in ms after action before doing same action
      rs.min_reversal_wait = 86400000 * 0.75 // wait in ms after action before doing opposite action
      rs.min_performance = -0.015 // abort trades with lower performance score
      if (first_run) {
        delete rs.real_trade_warning
      }
      cb()
    },
    // sync balance if key is present and we're in the `run` command
    function (tick, trigger, rs, cb) {
      if (get('command') !== 'run' || !c.gdax_key) {
        rs.start_balance = rs.sim_start_balance
        // add timestamp for simulations
        if (c.reporter_cols.indexOf('timestamp') === -1) {
          c.reporter_cols.unshift('timestamp')
          if (get('command') === 'run') {
            get('logger').info('trader', c.default_selector.grey, ('No trader API key provided. Starting in advisor mode. --Zen').yellow, {feed: 'trader'})
          }
        }
        if (get('command') === 'sim') {
          // change reporting interval for sims
          c.reporter_sizes = ['1h']
        }
        return cb()
      }
      if (!client) {
        client = new KrakenClient(kraken_key, kraken_secret)
        // client = new CoinbaseExchange.AuthenticatedClient(c.gdax_key, c.gdax_secret, c.gdax_passphrase)
      }

      client.api('Balance', null, function(err, data) {
        // not {}
        // if (err) {
        //   get('logger').info('kraken err bal', JSON.stringify(err))
        // }

        // HACK non 200
        if(!data) {
          return cb()
        }
          // if (resp.statusCode !== 200) {
          //   console.error(data)
          //   get('logger').error('non-200 status from exchange: ' + resp.statusCode, {data: {statusCode: resp.statusCode, body: accounts}})
          //   return cb()
          // }

          rs.balance = {}

          var accounts = data.result

          // var gdax_accounts = [
          //   {currency:'ZEUR', balance:accounts['ZEUR']},
          //   {currency:'XXBT', balance:accounts['XXBT']},
          //   {currency:'ZUSD', balance:accounts['ZUSD']}
          // ]

          var gdax_accounts = []
          Object.keys(accounts).map( key => gdax_accounts.push({currency: key, balance:accounts[key]}) ) 


          //get('logger').info('acc', JSON.stringify(gdax_accounts))

          gdax_accounts.forEach(function (account) {
            if (account.currency === rs.currency) {
              rs.balance[rs.currency] = n(account.balance).value()
            }
            else if (account.currency === rs.asset) {
              rs.balance[rs.asset] = n(account.balance).value()
            }
          })

          if (first_run) {
            sync_start_balance = true
          }
          var balance_sig = sig(rs.balance)
          if (balance_sig !== last_balance_sig) {
            get('logger').info('kraken balance', 'balance'.grey, n(rs.balance[rs.asset]).format('0.000').white, rs.asset.grey, n(rs.balance[rs.currency]).format('0.00').yellow, rs.currency.grey, {feed: 'exchange'})
            last_balance_sig = balance_sig
          }
        })
        if (first_run) {
          sync_start_balance = true
        }
        var balance_sig = sig(rs.balance)
        if (balance_sig !== last_balance_sig) {
          get('logger').info('trader', c.default_selector.grey, 'New account balance:'.cyan, n(rs.balance[rs.asset]).format('0.000').white, rs.asset.grey, format_currency(rs.balance[rs.currency], rs.currency).yellow, rs.currency.grey, {feed: 'trader'})
          if (!rs.real_trade_warning) {
            get('logger').info('trader', c.default_selector.grey, '"Starting REAL trading! Hold on to your butts!" --Zen'.cyan, {feed: 'trader'})
            rs.real_trade_warning = true
          }
          last_balance_sig = balance_sig
        }
        cb()

      // client.getAccounts(function (err, resp, accounts) {
      //   if (err) throw err
      //   if (resp.statusCode !== 200) {
      //     console.error(accounts)
      //     get('logger').error('non-200 status from exchange: ' + resp.statusCode, {data: {statusCode: resp.statusCode, body: accounts}})
      //     return cb()
      //   }
      //   rs.balance = {}
      //   accounts.forEach(function (account) {
      //     if (account.currency === rs.currency) {
      //       rs.balance[rs.currency] = n(account.balance).value()
      //     }
      //     else if (account.currency === rs.asset) {
      //       rs.balance[rs.asset] = n(account.balance).value()
      //     }
      //   })
      //   if (first_run) {
      //     sync_start_balance = true
      //   }
      //   var balance_sig = sig(rs.balance)
      //   if (balance_sig !== last_balance_sig) {
      //     get('logger').info('trader', c.default_selector.grey, '"Starting REAL trading! Hold on to your butts!" --Zen'.cyan, ' Balance:'.grey, n(rs.balance[rs.asset]).format('0.000').white, rs.asset.grey, n(rs.balance[rs.currency]).format('0.00').yellow, rs.currency.grey, {feed: 'exchange'})
      //     last_balance_sig = balance_sig
      //   }
      //   cb()
      // })
    },
    // record market price, balance, roi and stats
    function (tick, trigger, rs, cb) {
      // note the last close price
      var market_price = o(tick, rs.selector + '.close')
      // sometimes the tick won't have a close price for this selector.
      // keep old close price in memory.
      if (market_price) {
        rs.market_price = market_price
      }
      if (tick.size !== rs.check_period) {
        return cb()
      }
      delete rs.lookback_warning
      rs.ticks || (rs.ticks = 0)
      rs.progress || (rs.progress = 0)
      if (!rs.market_price) {
        //get('logger').info('trader', ('no close price for tick ' + tick.id).red, {feed: 'trader'})
        return cb()
      }
      if (!rs.balance) {
        // start with start_balance, neutral position
        rs.balance = {}
        rs.balance[rs.currency] = n(rs.start_balance).divide(2).value()
        rs.balance[rs.asset] = n(rs.start_balance).divide(2).divide(rs.market_price).value()
      }
      rs.consolidated_balance = n(rs.balance[rs.currency]).add(n(rs.balance[rs.asset]).multiply(rs.market_price)).value()
      if (sync_start_balance) {
        rs.start_balance = rs.consolidated_balance
        sync_start_balance = false
      }
      rs.roi = n(rs.consolidated_balance).divide(rs.start_balance).value()
      rs.ticks++
      cb()
    },
    // calculate first rsi from ticks lookback
    function (tick, trigger, rs, cb) {
      var rsi_tick_id = tb(tick.time).resize(rs.rsi_period).toString()
      if (rs.rsi && rs.rsi_tick_id && rs.rsi_tick_id !== rsi_tick_id) {
        // rsi period turnover. record last rsi for smoothing.
        rs.last_rsi = JSON.parse(JSON.stringify(rs.rsi))
        rs.rsi.samples++
        //console.error('last rsi', rs.last_rsi)
      }
      rs.rsi_tick_id = rsi_tick_id
      cb()
    },
    function (tick, trigger, rs, cb) {
      if (rs.first_rsi) {
        return cb()
      }
      // calculate first rsi
      //console.error('computing RSI', tick.id)
      var bucket = tb(tick.time).resize(rs.rsi_period)
      var params = {
        query: {
          app: get('app_name'),
          size: rs.rsi_period,
          time: {
            $lt: bucket.toMilliseconds()
          }
        },
        limit: rs.rsi_query_limit,
        sort: {
          time: -1
        }
      }
      params.query[rs.selector] = {$exists: true}
      get('ticks').select(params, function (err, lookback) {
        if (err) return cb(err)
        var missing = false
        if (lookback.length < rs.rsi_periods) {
          if (!rs.lookback_warning) {
            get('logger').info('trader', c.default_selector.grey, ('need more historical data, only have ' + lookback.length + ' of ' + rs.rsi_periods + ' ' + rs.rsi_period + ' ticks').yellow)
          }
          rs.lookback_warning = true
          return cb()
        }
        bucket.subtract(1)
        lookback.forEach(function (tick) {
          while (bucket.toMilliseconds() > tick.time) {
            get('logger').info('trader', c.default_selector.grey, ('missing RSI tick: ' + get_timestamp(bucket.toMilliseconds())).red)
            missing = true
            bucket.subtract(1)
          }
          //get('logger').info('trader', 'RSI tick OK:'.grey, get_timestamp(bucket.toMilliseconds()).green)
          bucket.subtract(1)
        })
        if (missing) {
          if (!rs.missing_warning) {
            get('logger').info('trader', c.default_selector.grey, 'missing tick data, RSI might be inaccurate. Try running `zenbot map --backfill` or wait for 3.6 for the new `zenbot repair` tool.'.red)
          }
          rs.missing_warning = true
        }
        if (!rs.missing_warning && !rs.rsi_complete_warning) {
          get('logger').info('trader', c.default_selector.grey, ('historical data OK! computing initial RSI from last ' + lookback.length + ' ' + rs.rsi_period + ' ticks').green)
          rs.rsi_complete_warning = true
        }
        withLookback(lookback.reverse())
      })
      function withLookback (lookback) {
        var init_lookback = lookback.slice(0, rs.rsi_periods + 1)
        var smooth_lookback = lookback.slice(rs.rsi_periods + 1)
        var de = o(init_lookback.pop(), rs.selector)
        var r = {}
        r.samples = init_lookback.length
        if (r.samples < rs.rsi_periods) {
          return cb()
        }
        r.close = de.close
        r.last_close = o(init_lookback[r.samples - 1], rs.selector).close
        r.current_gain = r.close > r.last_close ? n(r.close).subtract(r.last_close).value() : 0
        r.current_loss = r.close < r.last_close ? n(r.last_close).subtract(r.close).value() : 0
        var prev_close = 0
        var gain_sum = init_lookback.reduce(function (prev, curr) {
          curr = o(curr, rs.selector)
          if (!prev_close) {
            prev_close = curr.close
            return 0
          }
          var gain = curr.close > prev_close ? curr.close - prev_close : 0
          prev_close = curr.close
          return n(prev).add(gain).value()
        }, 0)
        var avg_gain = n(gain_sum).divide(r.samples).value()
        prev_close = 0
        var loss_sum = init_lookback.reduce(function (prev, curr) {
          curr = o(curr, rs.selector)
          if (!prev_close) {
            prev_close = curr.close
            return 0
          }
          var loss = curr.close < prev_close ? prev_close - curr.close : 0
          prev_close = curr.close
          return n(prev).add(loss).value()
        }, 0)
        var avg_loss = n(loss_sum).divide(r.samples).value()
        r.last_avg_gain = avg_gain
        r.last_avg_loss = avg_loss
        r.avg_gain = n(r.last_avg_gain).multiply(rs.rsi_periods - 1).add(r.current_gain).divide(rs.rsi_periods).value()
        r.avg_loss = n(r.last_avg_loss).multiply(rs.rsi_periods - 1).add(r.current_loss).divide(rs.rsi_periods).value()
        if (r.avg_loss === 0) {
          r.value = r.avg_gain ? 100 : 50
        }
        else {
          r.relative_strength = n(r.avg_gain).divide(r.avg_loss).value()
          r.value = n(100).subtract(n(100).divide(n(1).add(r.relative_strength))).value()
        }
        r.ansi = n(r.value).format('0')[r.value > 70 ? 'green' : r.value < 30 ? 'red' : 'white']
        // first rsi, calculated from prev 14 ticks
        rs.last_rsi = JSON.parse(JSON.stringify(r))
        //console.error('first rsi', r)
        rs.rsi = r
        rs.first_rsi = rs.last_rsi = JSON.parse(JSON.stringify(r))
        smooth_lookback.forEach(function (de) {
          r.last_close = r.close
          r.close = o(de, rs.selector).close
          r.samples++
          r.current_gain = r.close > r.last_close ? n(r.close).subtract(r.last_close).value() : 0
          r.current_loss = r.close < r.last_close ? n(r.last_close).subtract(r.close).value() : 0
          r.last_avg_gain = r.avg_gain
          r.last_avg_loss = r.avg_loss
          r.avg_gain = n(r.last_avg_gain).multiply(rs.rsi_periods - 1).add(r.current_gain).divide(rs.rsi_periods).value()
          r.avg_loss = n(r.last_avg_loss).multiply(rs.rsi_periods - 1).add(r.current_loss).divide(rs.rsi_periods).value()
          if (r.avg_loss === 0) {
            r.value = r.avg_gain ? 100 : 50
          }
          else {
            r.relative_strength = n(r.avg_gain).divide(r.avg_loss).value()
            r.value = n(100).subtract(n(100).divide(n(1).add(r.relative_strength))).value()
          }
          r.ansi = n(r.value).format('0')[r.value > 70 ? 'green' : r.value < 30 ? 'red' : 'white']
          rs.last_rsi = JSON.parse(JSON.stringify(r))
          //console.error('smooth', r.close, r.last_close, r.ansi)
        })
        cb()
      }
    },
    // calculate the smoothed rsi if we have last_rsi
    function (tick, trigger, rs, cb) {
      if (!rs.market_price || !rs.rsi || !rs.last_rsi) {
        return cb()
      }
      var r = rs.rsi
      //console.error('market', rs.market_price, rs.rsi)
      r.close = rs.market_price
      r.last_close = rs.last_rsi.close
      r.current_gain = r.close > r.last_close ? n(r.close).subtract(r.last_close).value() : 0
      r.current_loss = r.close < r.last_close ? n(r.last_close).subtract(r.close).value() : 0
      r.last_avg_gain = rs.last_rsi.avg_gain
      r.last_avg_loss = rs.last_rsi.avg_loss
      r.avg_gain = n(r.last_avg_gain).multiply(rs.rsi_periods - 1).add(r.current_gain).divide(rs.rsi_periods).value()
      r.avg_loss = n(r.last_avg_loss).multiply(rs.rsi_periods - 1).add(r.current_loss).divide(rs.rsi_periods).value()
      if (r.avg_loss === 0) {
        r.value = r.avg_gain ? 100 : 50
      }
      else {
        r.relative_strength = n(r.avg_gain).divide(r.avg_loss).value()
        r.value = n(100).subtract(n(100).divide(n(1).add(r.relative_strength))).value()
      }
      r.ansi = n(r.value).format('0')[r.value > 70 ? 'green' : r.value < 30 ? 'red' : 'white']
      //console.error('smooth 2', r.close, r.last_close, r.ansi)
      //process.exit()
      cb()
    },
    // detect trends from rsi
    function (tick, trigger, rs, cb) {
      var trend
      var r = rs.rsi
      if (!r) {
        return cb()
      }
      if (r.samples < rs.rsi_periods) {
        if (!rs.rsi_warning) {
          // get('logger').info('trader', c.default_selector.grey, (rs.rsi_period + ' RSI: not enough samples for tick ' + rs.rsi_tick_id + ': ' + rs.rsi.samples).red, {feed: 'trader'})
        }
        rs.rsi_warning = true
      }
      else {
        if (r.value >= rs.rsi_up) {
          trend = 'UP'
        }
        else if (r.value <= rs.rsi_down) {
          trend = 'DOWN'
        }
        else {
          trend = null
        }
      }
      if (trend !== rs.trend) {
        get('logger').info('trader', c.default_selector.grey, 'RSI:'.grey + r.ansi, ('trend: ' + rs.trend + ' -> ' + trend).yellow, {feed: 'trader'})
        delete rs.balance_warning
        delete rs.roi_warning
        delete rs.rsi_warning
        delete rs.delta_warning
        delete rs.buy_warning
        delete rs.perf_warning
        delete rs.action_warning
        delete rs.trend_warning
      }
      rs.trend = trend
      cb()
    },
    // @todo MACD
    function (tick, trigger, rs, cb) {
      cb()
    },
    // trigger trade signals from trends
    function (tick, trigger, rs, cb) {
      if (tick.size !== rs.check_period) {
        return cb()
      }
      // for run command, don't trade unless this is a new tick
      if (get('command') !== 'sim' && tick.time < start) {
        get('logger').info('trader', c.default_selector.grey, ('skipping historical tick ' + tick.id).grey, {feed: 'trader'})
        return cb()
      }
      if (rs.trend) {
        if (!rs.trend_warning && !rs.balance) {
          get('logger').info('trader', c.default_selector.grey, ('no balance to act on trend: ' + rs.trend + '!').red, {feed: 'trader'})
          rs.trend_warning = true
        }
        else if (!rs.trend_warning && !rs.market_price) {
          get('logger').info('trader', c.default_selector.grey, ('no market_price to act on trend: ' + rs.trend + '!').red, {feed: 'trader'})
          rs.trend_warning = true
        }
      }
      rs.progress = 1
      if (rs.trend && rs.balance && rs.market_price) {
        var size, new_balance = {}
        if (rs.trend === 'DOWN') {
          // calculate sell size
          size = rs.balance[rs.asset]
        }
        else if (rs.trend === 'UP') {
          // calculate buy size
          size = n(rs.balance[rs.currency]).divide(rs.market_price).value()
        }
        size = n(size || 0).multiply(rs.trade_pct).value()
        if (rs.trend === 'DOWN') {
          // SELL!
          if (rs.last_sell_time && tick.time - rs.last_sell_time <= rs.min_double_wait) {
            if (!rs.sell_warning) {
              get('logger').info('trader', c.default_selector.grey, ('too soon to sell after sell! waiting ' + get_duration(n(rs.min_double_wait).subtract(n(tick.time).subtract(rs.last_sell_time)).multiply(1000).value())).red, {feed: 'trader'})
            }
            rs.sell_warning = true
            return cb()
          }
          if (rs.last_buy_time && tick.time - rs.last_buy_time <= rs.min_reversal_wait) {
            if (!rs.sell_warning) {
              get('logger').info('trader', c.default_selector.grey, ('too soon to sell after buy! waiting ' + get_duration(n(rs.min_reversal_wait).subtract(n(tick.time).subtract(rs.last_buy_time)).multiply(1000).value())).red, {feed: 'trader'})
            }
            rs.sell_warning = true
            return cb()
          }
          new_balance[rs.currency] = n(rs.balance[rs.currency]).add(n(size).multiply(rs.market_price)).value()
          new_balance[rs.asset] = n(rs.balance[rs.asset]).subtract(size).value()
          rs.op = 'sell'
          if (!rs.action_warning) {
            get('logger').info('trader', c.default_selector.grey, ('attempting to sell ' + n(size).format('0.00000000') + ' ' + rs.asset + ' for ' + format_currency(n(size).multiply(rs.market_price).value(), rs.currency) + ' ' + rs.currency).yellow, {feed: 'trader'})
          }
          rs.action_warning = true
        }
        else if (rs.trend === 'UP') {
          // BUY!
          if (rs.last_buy_time && tick.time - rs.last_buy_time <= rs.min_double_wait) {
            if (!rs.buy_warning) {
              get('logger').info('trader', c.default_selector.grey, ('too soon to buy after buy! waiting ' + get_duration(n(rs.min_double_wait).subtract(n(tick.time).subtract(rs.last_buy_time)).multiply(1000).value())).red, {feed: 'trader'})
            }
            rs.buy_warning = true
            return cb()
          }
          if (rs.last_sell_time && tick.time - rs.last_sell_time <= rs.min_reversal_wait) {
            if (!rs.buy_warning) {
              get('logger').info('trader', c.default_selector.grey, ('too soon to buy after sell! waiting ' + get_duration(n(rs.min_reversal_wait).subtract(n(tick.time).subtract(rs.last_sell_time)).multiply(1000).value())).red, {feed: 'trader'})
            }
            rs.buy_warning = true
            return cb()
          }
          new_balance[rs.asset] = n(rs.balance[rs.asset]).add(size).value()
          new_balance[rs.currency] = n(rs.balance[rs.currency]).subtract(n(size).multiply(rs.market_price)).value()
          rs.op = 'buy'
          if (!rs.action_warning) {
            get('logger').info('trader', c.default_selector.grey, ('attempting to buy ' + n(size).format('0.00000000') + ' ' + rs.asset + ' for ' + format_currency(n(size).multiply(rs.market_price).value(), rs.currency) + ' ' + rs.currency).yellow, {feed: 'trader'})
          }
          rs.action_warning = true
        }
        else {
          // unknown trend
          get('logger').info('trader', c.default_selector.grey, ('unkown trend (' + rs.trend + ') aborting trade!').red, {feed: 'trader'})
          return cb()
        }
        // min size
        if (!size || size < rs.min_trade) {
          if (!rs.balance_warning) {
            get('logger').info('trader', c.default_selector.grey, 'trend: '.grey, rs.trend, ('not enough funds (' + (rs.op === 'sell' ? n(size).format('0.00000000') : format_currency(rs.balance[rs.currency], rs.currency)) + ' ' + (rs.op === 'sell' ? rs.asset : rs.currency) + ') to execute min. ' + rs.op + ' ' + rs.min_trade + ', aborting trade!').red, {feed: 'trader'})
          }
          rs.balance_warning = true
          return cb()
        }
        // fee calc
        rs.fee = n(size).multiply(rs.market_price).multiply(rs.fee_pct).value()
        new_balance[rs.currency] = n(new_balance[rs.currency]).subtract(rs.fee).value()
        // consolidate balance
        rs.new_end_balance = n(new_balance[rs.currency]).add(n(new_balance[rs.asset]).multiply(rs.market_price)).value()
        rs.new_roi = n(rs.new_end_balance).divide(rs.start_balance).value()
        rs.new_roi_delta = n(rs.new_roi).subtract(rs.roi || 0).value()

        if (rs.op === 'buy') {
          // % drop
          rs.performance = rs.last_sell_price ? n(rs.last_sell_price).subtract(rs.market_price).divide(rs.last_sell_price).value() : null
        }
        else {
          // % gain
          rs.performance = rs.last_buy_price ? n(rs.market_price).subtract(rs.last_buy_price).divide(rs.last_buy_price).value() : null
        }
        if (rs.min_performance && rs.performance !== null && rs.performance < rs.min_performance) {
          if (!rs.perf_warning) {
            get('logger').info('trader', c.default_selector.grey, ('aborting ' + rs.op + ' due to low perf. = ' + n(rs.performance).format('0.000')).red, {feed: 'trader'})
          }
          rs.perf_warning = true
          return cb()
        }
        if (rs.op === 'buy') {
          rs.waited = rs.last_sell_time ? get_duration(n(tick.time).subtract(rs.last_sell_time).multiply(1000).value()) : null
          rs.last_buy_time = tick.time
        }
        else {
          rs.waited = rs.last_buy_time ? get_duration(n(tick.time).subtract(rs.last_buy_time).multiply(1000).value()) : null
          rs.last_sell_time = tick.time
        }
        rs.performance_scores || (rs.performance_scores = [])
        rs.performance_scores.push(rs.performance)
        var performance_sum = rs.performance_scores.reduce(function (prev, curr) {
          return prev + curr
        }, 0)
        rs.performance_avg = n(performance_sum).divide(rs.performance_scores.length).value()
        rs.balance = new_balance
        rs.end_balance = rs.new_end_balance
        rs.roi = rs.new_roi
        rs.num_trades || (rs.num_trades = 0)
        rs.num_trades++
        var trade = {
          type: rs.op,
          asset: rs.asset,
          currency: rs.currency,
          exchange: rs.exchange,
          price: rs.market_price,
          fee: rs.fee,
          market: true,
          size: size,
          rsi: rs.rsi.value,
          roi: rs.roi,
          roi_delta: rs.new_roi_delta,
          performance: rs.performance,
          waited: rs.waited,
          balance: new_balance,
          end_balance: rs.new_end_balance
        }
        trigger(trade)
        if (client) {

          const order = {
            // pair: 'XXBTZUSD',
            pair: rs.asset+''+rs.currency,
            type: rs.op,
            ordertype: 'market', // market
            //price: 0.01, //optional (in second currency)
            volume: size //(first value)
          }

          client.api('AddOrder', order, function(err, data) {
              if(err) {
                console.log(err)
              }
              else {
                console.log(data.result)
                onOrder(err, data.result)
              }
          })

          // var params = {
          //   type: 'market',
          //   size: n(size).format('0.000000'),
          //   product_id: rs.asset + '-' + rs.currency
          // }
          // client[rs.op](params, function (err, resp, order) {
          //   onOrder(err, resp, order)
          // })
        }
        else if (!rs.sim_warning) {
          get('logger').info('trader', c.default_selector.grey, ('Relax! This is a simulated trade! No real transaction will take place. --Zen').yellow, {feed: 'trader'})
          rs.sim_warning = true
        }
        if (rs.op === 'buy') {
          rs.last_buy_price = rs.market_price
        }
        else {
          rs.last_sell_price = rs.market_price
        }
        rs.last_op = rs.op
      }
      cb()
    },
    function (tick, trigger, rs, cb) {
      first_run = false
      cb()
    }
    // END DEFAULT TRADE LOGIC
  ]
}

@Labseb
Copy link

Labseb commented Aug 31, 2016

thank's...and the last one, your config_btc_usd.js ?
if you change something somewhere else....

@grigio
Copy link
Contributor Author

grigio commented Aug 31, 2016

var c = module.exports = require('./config')

c.assets = [
"XXBT"
]
c.currencies = [
"ZUSD",
"XXBT"
]

// default selector for indicators, etc
c.default_selector = "kraken.XXBT-ZUSD"

@Labseb
Copy link

Labseb commented Aug 31, 2016

it's alive !!!!!
certainly an error in my default_logic.js.
Thank's a lot

@backenders-game
Copy link

Thank you for help, grigio.
It works for me too. Seems c.assets and c.currencies in config_btc_usd.js was the issue.

Is your last posted logic is less than 3.5.15 (rsi fix) version?

@grigio
Copy link
Contributor Author

grigio commented Aug 31, 2016

@Sorrow2 the last logic attached is adapted from the current one in zenbot master.

@Labseb
Copy link

Labseb commented Aug 31, 2016

New error:
[ trader] kraken.XXBT-ZEUR No trader API key provided. Starting in advisor mode. --Zen
but i wrote my API key and Private key in default_logic.js....
I missed something ?

In default_logic there is:
// sync balance if key is present and we're in the run command
function (tick, trigger, rs, cb) {
if (get('command') !== 'run' || !c.gdax_key) {

So, if we don't give gdax_key.....
i propose:
// sync balance if key is present and we're in the run command
function (tick, trigger, rs, cb) {
if (get('command') !== 'run') {

It's appear ok, but it don't return my balance.... need to search again.

@backenders-game
Copy link

i added strings to c.gdax_key and secret in config
and it shows balance and real trades

@Labseb
Copy link

Labseb commented Aug 31, 2016

it's your correct balance ?

@backenders-game
Copy link

yes

@Labseb
Copy link

Labseb commented Sep 1, 2016

Everything work fine now.
Perfect for me now.
Perhaps we could make an how-to to connect with Kraken ?

@grigio
Copy link
Contributor Author

grigio commented Sep 1, 2016

@Labseb Maybe when default_logic.js will be exchange agnostic and stable, I'll cleanup the code and document it :P

@grigio
Copy link
Contributor Author

grigio commented Sep 2, 2016

BUG!

I noticed that, for some reasons to investigate, the Kraken trades stop silently to be reported after a certain time. I don't see any error also in the console logs.

In these logs I've last kraken event at 2:49 and then nothing.. but at 8:10 I restarted zenbot and I get 301 missing trades. /cc @carlos8f

And everytime I restart I lose the ROI .. 😞

Do you confirm? Do you know if it something kraken specific or it happens also for other exchanges?
It seems zenbot can usually recover from missing internet connection, but some times not and it gets stuck.

09/02/2016 08:11:07 AM CEST [      runner] m24546610 running [link]
09/02/2016 08:11:03 AM CEST [      kraken]    m24546610     301 trades. 09/02/2016 08:10:55 AM CEST SELL       148.647 at       508.520 XXBT/ZEUR [link]
09/02/2016 08:10:59 AM CEST [      kraken]    m24546610     301 trades. 09/02/2016 08:10:55 AM CEST SELL       148.647 at       508.520 XXBT/ZEUR [link]
09/02/2016 08:10:57 AM CEST [    launcher] cmd `reduce` booting [link]
09/02/2016 08:10:57 AM CEST [    launcher] cmd `map` booting [link]
09/02/2016 08:10:57 AM CEST [      runner] waiting for next 1d tick... [link]
09/02/2016 08:10:57 AM CEST [      runner] waiting for next 6h tick... [link]
09/02/2016 08:10:57 AM CEST [      runner] waiting for next 1h tick... [link]
09/02/2016 08:10:57 AM CEST [      runner] waiting for next 15m tick... [link]
09/02/2016 08:10:57 AM CEST [      runner] waiting for next 5m tick... [link]
09/02/2016 08:10:57 AM CEST [      runner] waiting for next 1m tick... [link]
09/02/2016 08:10:57 AM CEST [      runner] starting [link]
09/02/2016 08:10:57 AM CEST [    launcher] cmd `run` booting [link]
09/02/2016 08:10:55 AM CEST [    launcher] cmd `launch` booting [link]
09/02/2016 08:10:55 AM CEST [    launcher] cmd `launch` booting [link]
09/02/2016 08:10:43 AM CEST [    launcher] saved run_state, id = zb_run_xxbt_zeur [link]
09/02/2016 08:10:43 AM CEST [    launcher] saved run_state, id = zb_reduce [link]
09/02/2016 08:10:43 AM CEST [    launcher] saved run_state, id = zb_launch_xxbt_zeur [link]
09/02/2016 08:10:43 AM CEST [    launcher] saved run_state, id = zb_launch [link]
09/02/2016 08:10:43 AM CEST [    launcher] saved run_state, id = zb_map_xxbt_zeur [link]

09/02/2016 08:10:43 AM CEST [    launcher] cmd `run` exiting [link]
09/02/2016 08:10:43 AM CEST [    launcher] cmd `reduce` exiting [link]
09/02/2016 08:10:43 AM CEST [    launcher] cmd `launch` exiting [link]
09/02/2016 08:10:43 AM CEST [    launcher] cmd `map` exiting [link]
09/02/2016 08:10:43 AM CEST [    launcher] cmd `launch` exiting [link]
09/02/2016 08:00:08 AM CEST [      runner] waiting for next 6h tick... [link]
09/02/2016 08:00:03 AM CEST [      runner] waiting for next 6h tick... [link]
09/02/2016 08:00:03 AM CEST [      trader] kraken.XXBT-ZEUR 6h68184 running logic XXBT ZEUR [link]
09/02/2016 08:00:03 AM CEST [      runner] 6h68184 running [link]
09/02/2016 08:00:03 AM CEST [      trader] kraken.XXBT-ZEUR 6h68184 running logic XXBT ZEUR [link]
09/02/2016 08:00:03 AM CEST [      runner] 6h68184 running [link]
09/02/2016 03:00:04 AM CEST [      runner] waiting for next 1h tick... [link]
09/02/2016 03:00:04 AM CEST [      trader] kraken.XXBT-ZEUR h409104 running logic XXBT ZEUR [link]
09/02/2016 03:00:04 AM CEST [      runner] h409104 running [link]
09/02/2016 03:00:01 AM CEST [      runner] waiting for next 15m tick... [link]
09/02/2016 03:00:01 AM CEST [      trader] kraken.XXBT-ZEUR 15m1636419 running logic XXBT ZEUR [link]
09/02/2016 03:00:01 AM CEST [      runner] 15m1636419 running [link]
09/02/2016 02:50:05 AM CEST [      runner] waiting for next 5m tick... [link]
09/02/2016 02:50:05 AM CEST [      runner] waiting for next 5m tick... [link]
09/02/2016 02:50:00 AM CEST [      runner] waiting for next 5m tick... [link]
09/02/2016 02:50:00 AM CEST [      trader] kraken.XXBT-ZEUR 5m4909257 running logic XXBT ZEUR [link]
09/02/2016 02:50:00 AM CEST [      runner] 5m4909257 running [link]
09/02/2016 02:50:00 AM CEST [      trader] kraken.XXBT-ZEUR 5m4909257 running logic XXBT ZEUR [link]
09/02/2016 02:50:00 AM CEST [      runner] 5m4909257 running [link]
09/02/2016 02:49:04 AM CEST [      kraken]    m24546288       7 trades. 09/02/2016 02:48:59 AM CEST  BUY         3.465 at       508.753 XXBT/ZEUR [link]

@carlos8f
Copy link
Contributor

carlos8f commented Sep 2, 2016

I haven’t noticed any stability problems with my deployment using GDAX. It’s either Kraken-specific, or somehow your mapper or reducer process got stuck… hmmm.

On Sep 1, 2016, at 11:38 PM, Luigi Maselli notifications@github.com wrote:

BUG!

I noticed that, for some reasons to investigate, the Kraken trades stop silently to be reported after a certain time. I don't see any error also in the console logs.

In these logs I've last kraken event at 2:49 and then nothing.. but at 8:10 I restarted zenbot and I get 301 missing trades.

Do you confirm? Do you know if it something kraken specific or it happens also for other exchanges?
It seems zenbot can usually recover from missing internet connection, but some it gets stuck.

09/02/2016 08:11:07 AM CEST [ runner] m24546610 running [link]
09/02/2016 08:11:03 AM CEST [ kraken] m24546610 301 trades. 09/02/2016 08:10:55 AM CEST SELL 148.647 at 508.520 XXBT/ZEUR [link]
09/02/2016 08:10:59 AM CEST [ kraken] m24546610 301 trades. 09/02/2016 08:10:55 AM CEST SELL 148.647 at 508.520 XXBT/ZEUR [link]
09/02/2016 08:10:57 AM CEST [ launcher] cmd reduce booting [link]
09/02/2016 08:10:57 AM CEST [ launcher] cmd map booting [link]
09/02/2016 08:10:57 AM CEST [ runner] waiting for next 1d tick... [link]
09/02/2016 08:10:57 AM CEST [ runner] waiting for next 6h tick... [link]
09/02/2016 08:10:57 AM CEST [ runner] waiting for next 1h tick... [link]
09/02/2016 08:10:57 AM CEST [ runner] waiting for next 15m tick... [link]
09/02/2016 08:10:57 AM CEST [ runner] waiting for next 5m tick... [link]
09/02/2016 08:10:57 AM CEST [ runner] waiting for next 1m tick... [link]
09/02/2016 08:10:57 AM CEST [ runner] starting [link]
09/02/2016 08:10:57 AM CEST [ launcher] cmd run booting [link]
09/02/2016 08:10:55 AM CEST [ launcher] cmd launch booting [link]
09/02/2016 08:10:55 AM CEST [ launcher] cmd launch booting [link]
09/02/2016 08:10:43 AM CEST [ launcher] saved run_state, id = zb_run_xxbt_zeur [link]
09/02/2016 08:10:43 AM CEST [ launcher] saved run_state, id = zb_reduce [link]
09/02/2016 08:10:43 AM CEST [ launcher] saved run_state, id = zb_launch_xxbt_zeur [link]
09/02/2016 08:10:43 AM CEST [ launcher] saved run_state, id = zb_launch [link]
09/02/2016 08:10:43 AM CEST [ launcher] saved run_state, id = zb_map_xxbt_zeur [link]

09/02/2016 08:10:43 AM CEST [ launcher] cmd run exiting [link]
09/02/2016 08:10:43 AM CEST [ launcher] cmd reduce exiting [link]
09/02/2016 08:10:43 AM CEST [ launcher] cmd launch exiting [link]
09/02/2016 08:10:43 AM CEST [ launcher] cmd map exiting [link]
09/02/2016 08:10:43 AM CEST [ launcher] cmd launch exiting [link]
09/02/2016 08:00:08 AM CEST [ runner] waiting for next 6h tick... [link]
09/02/2016 08:00:03 AM CEST [ runner] waiting for next 6h tick... [link]
09/02/2016 08:00:03 AM CEST [ trader] kraken.XXBT-ZEUR 6h68184 running logic XXBT ZEUR [link]
09/02/2016 08:00:03 AM CEST [ runner] 6h68184 running [link]
09/02/2016 08:00:03 AM CEST [ trader] kraken.XXBT-ZEUR 6h68184 running logic XXBT ZEUR [link]
09/02/2016 08:00:03 AM CEST [ runner] 6h68184 running [link]
09/02/2016 03:00:04 AM CEST [ runner] waiting for next 1h tick... [link]
09/02/2016 03:00:04 AM CEST [ trader] kraken.XXBT-ZEUR h409104 running logic XXBT ZEUR [link]
09/02/2016 03:00:04 AM CEST [ runner] h409104 running [link]
09/02/2016 03:00:01 AM CEST [ runner] waiting for next 15m tick... [link]
09/02/2016 03:00:01 AM CEST [ trader] kraken.XXBT-ZEUR 15m1636419 running logic XXBT ZEUR [link]
09/02/2016 03:00:01 AM CEST [ runner] 15m1636419 running [link]
09/02/2016 02:50:05 AM CEST [ runner] waiting for next 5m tick... [link]
09/02/2016 02:50:05 AM CEST [ runner] waiting for next 5m tick... [link]
09/02/2016 02:50:00 AM CEST [ runner] waiting for next 5m tick... [link]
09/02/2016 02:50:00 AM CEST [ trader] kraken.XXBT-ZEUR 5m4909257 running logic XXBT ZEUR [link]
09/02/2016 02:50:00 AM CEST [ runner] 5m4909257 running [link]
09/02/2016 02:50:00 AM CEST [ trader] kraken.XXBT-ZEUR 5m4909257 running logic XXBT ZEUR [link]
09/02/2016 02:50:00 AM CEST [ runner] 5m4909257 running [link]
09/02/2016 02:49:04 AM CEST [ kraken] m24546288 7 trades. 09/02/2016 02:48:59 AM CEST BUY 3.465 at 508.753 XXBT/ZEUR [link]


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

@grigio
Copy link
Contributor Author

grigio commented Sep 2, 2016

I forgot to say that my connection is unstable, sometimes I've slow latency but also Kraken is buggy yesterday I got a lot clodflare pages instead of the actual API response.

@carlos8f is there a way to recover the ROI from the previous session? or a better way to deal with this situation without losing the ROI? Thanks

@carlos8f
Copy link
Contributor

carlos8f commented Sep 2, 2016

@grigio if you know what your start balance was, you can recover the ROI calculation with:

mongo
use zenbrain
var rs = db.run_states.findOne({_id: 'zb_run_xxbt_zuer'})
rs.start_balance = (consolidated balance)
db.run_states.save(rs)

@carlos8f
Copy link
Contributor

carlos8f commented Sep 2, 2016

as a note, the ROI reset I'm probably going to make configurable in 3.6 so you can decide when/if you want it reset

@grigio
Copy link
Contributor Author

grigio commented Sep 9, 2016

After several hours of usage I've this issue #9 (comment) @Labseb @Sorrow2 do you confirm?

This was referenced Sep 10, 2016
@Labseb
Copy link

Labseb commented Sep 12, 2016

i confirm, same problem. need to restart it

@morphax
Copy link

morphax commented Dec 5, 2016

Got an error for a few days, you know what maybe wrong? Tx
`crypto.js:67

this._handle.update(data, encoding);
^
TypeError: Data must be a string or a buffer
at TypeError (native)
at Hash.update (crypto.js:67:16)
at module.exports (/home/zenbot/zenbot/node_modules/sig/index.js:5:36)
at /home/zenbot/zenbot/kraken_logic.js:185:27
at Immediate.doNext [as _onImmediate] (/home/zenbot/zenbot/node_modules/zenbrain/utils/apply_funcs.js:10:12)
at tryOnImmediate (timers.js:543:15)
at processImmediate [as _immediateCallback] (timers.js:523:5)`

UPDATE: the --backfill startup option solved in fact the RSI problem.
The message " missing tick data, RSI might be inaccurate. Try running zenbot map --backfill or wait for 3.6 for the new zenbot repair tool." was shown only after the the key and secret were removed

@carlos8f
Copy link
Contributor

Zenbot 4 is out, so I'm cleaning up 3.x issues. Please use and test 4, thanks!

This issue was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants