#! /usr/bin/ruby

$stdout.sync = true 
$stdin.sync = true  
$LOAD_PATH << './lib' << '../coinbasepro-api/lib/'

require 'coinbasepro/api'
require 'config'
require 'exchange'
require 'logic'
require 'message'

def debug(msg)
  time = Time.new.inspect
  puts "#{time}: #{msg}"
end

@config = Config.new
@exchange = Exchange.new(@config.content)
@logic = Logic.new(@exchange)
@ex_config = @config.content[@exchange.exchange]
@message = Message.new(@config)

def updateLastOrders(toUpdate)
  #debug "updateLastOrders: " + toUpdate.to_s
  toUpdate.each do |i|
    if i != @ex_config['config']['quote_currency']
      product_id = i + '-' + @ex_config['config']['quote_currency']
      ldos = @exchange.last_done_orders(product_id: product_id)
      @last_orders[i] = ldos
    end
  end
end

def updateBalances(toUpdate)
  #debug "updateBalances: " + toUpdate.to_s
  toUpdate.each do |i|
    @balance[i] = @exchange.balance(currency: i)
  end
end

def main
  begin
    granularity = @ex_config['config']['granularity']
    @message.send("Starting granularity=#{granularity}")
    @last_orders = {}
    @balance = {}
    prev_ma = nil
    toUpdate = @ex_config['config']['base_currencies'] + Array(@ex_config['config']['quote_currency'])
    while true
      time = Time.new.to_i
      updateLastOrders(toUpdate)
      updateBalances(toUpdate)
      toUpdate = [ @ex_config['config']['quote_currency'] ]
      @ex_config['config']['base_currencies'].each do |bc|
        history = @exchange.getHistory(product_id: bc + '-' + @ex_config['config']['quote_currency'], granularity: granularity)
        @logic.loadHistory(history, bc)
        analyze = @logic.analyze
        debug "#{bc}: #{analyze}"
  
        base_balance = @balance[bc]
        quote_balance = @balance[@ex_config['config']['quote_currency']]
        if base_balance['hold'].to_f > 0
          toUpdate = (toUpdate + Array(bc)).uniq
        end
        product_id = bc + '-' + @ex_config['config']['quote_currency']
        lt = @exchange.last_trade(product_id: product_id)
        debug "#{bc}: last_trade #{lt}"
        if @last_orders[bc][0].nil?
          last_side = 'sell'
        else
          last_side = @last_orders[bc][0]['side'] || nil
        end
        if last_side == 'buy'
          profit = @logic.profit(buy: @last_orders[bc][0], last_trade: lt)
          debug "#{bc}: Current profits perc=%0.2f cash=%0.4f" % [profit['perc'], profit['cash']]
          latest_buy = @exchange.latest_buy(product_id)
          last_profit = @logic.profit(buy: latest_buy, last_trade: lt)
          debug "#{bc}: Current profits since latest buy perc=%0.2f cash=%0.4f" % [last_profit['perc'], last_profit['cash']]
          if last_profit['perc'] < -10
            if quote_balance['available'].to_f < @ex_config['config']['buysize']
              debug "#{bc}: Not enough funds for extra buy avaiable=(#{quote_balance['available']})"
            else
              spend = @ex_config['config']['buysize']
              @message.send("#{bc}: extra buy order spend=#{spend}")
              @exchange.direct_buy(product_id: product_id, spend: spend)
            end
            toUpdate = (toUpdate + Array(bc)).uniq
          elsif analyze['bottom'] and base_balance['hold'].to_f > 0
            debug "#{bc}: Cancel orders"
            @exchange.cancel_orders(product_id: product_id)
          elsif (analyze['top'] or analyze['all_down']) and profit['perc'] > 1 and analyze['alpha1'].to_f > 0.2
            if base_balance['hold'].to_f > 0
              debug "#{bc}: On hold orders, not selling"
            else
              size = base_balance['available']
              @message.send("#{bc}: sell order size=#{size}")
              @exchange.lossless_sell(product_id: product_id, size: size)
              toUpdate = (toUpdate + Array(bc)).uniq
            end
          end
        else
          if analyze['bottom'] or analyze['all_up']
            if base_balance['hold'].to_f > 0
              debug "#{bc}: On hold orders, not buying"
            elsif quote_balance['available'].to_f < @ex_config['config']['buysize']
              debug "#{bc}: Not enough funds to buy avaiable=(#{quote_balance['available']})"
            else
              spend = @ex_config['config']['buysize']
              @message.send("#{bc}: buy order spend=#{spend}")
              @exchange.direct_buy(product_id: product_id, spend: spend)
              toUpdate = (toUpdate + Array(bc)).uniq
            end
          end
        end
      end
      sleepTime = (granularity - (Time.new.to_i - time))
      if sleepTime > 0
        sleep sleepTime
      end
    end
  rescue => e
    msg = e.to_s + "\n" + e.backtrace.join("\n")
    @message.send(msg, false)
    raise
  end
end

main
