bitex_bot 0.9.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/lib/bitex_bot/models/api_wrappers/api_wrapper.rb +15 -4
- data/lib/bitex_bot/models/api_wrappers/bitex/bitex_api_wrapper.rb +2 -1
- data/lib/bitex_bot/models/api_wrappers/itbit/itbit_api_wrapper.rb +11 -3
- data/lib/bitex_bot/models/api_wrappers/kraken/kraken_api_wrapper.rb +14 -11
- data/lib/bitex_bot/models/api_wrappers/kraken/kraken_order.rb +13 -8
- data/lib/bitex_bot/models/buy_opening_flow.rb +8 -0
- data/lib/bitex_bot/models/closing_flow.rb +26 -5
- data/lib/bitex_bot/models/opening_flow.rb +20 -10
- data/lib/bitex_bot/models/sell_opening_flow.rb +8 -0
- data/lib/bitex_bot/robot.rb +24 -10
- data/lib/bitex_bot/version.rb +1 -1
- metadata +2 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 84beba74777fdc3e278589d5caf543ecba824116
         | 
| 4 | 
            +
              data.tar.gz: 9dd73af6332116ece29196635b7b057114333781
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: '0438c07f2f3f8111b9de9b66e5f942ef193f9036c9f8136739b77dc1933f2a73cf020e2f6a6fccf445e2450a5b98e163f1959871cab4a52bdc070c030fdd2b46'
         | 
| 7 | 
            +
              data.tar.gz: 7296518d188e5ea3ac4e12128585b0f1701e08a76cd60405caa7c3833f92635783163aa3ed97a00fb58491a9fa9469d1caa73aafa934d616ca8bba15586f60fe
         | 
    
        data/.rubocop.yml
    CHANGED
    
    
| @@ -62,6 +62,10 @@ class ApiWrapper | |
| 62 62 | 
             
                :timestamp    # Epoch Integer
         | 
| 63 63 | 
             
              )
         | 
| 64 64 |  | 
| 65 | 
            +
              def name
         | 
| 66 | 
            +
                self.class.name.underscore.split('_').first.capitalize
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
             | 
| 65 69 | 
             
              # @return [Array<Transaction>]
         | 
| 66 70 | 
             
              def transactions
         | 
| 67 71 | 
             
                raise 'self subclass responsibility'
         | 
| @@ -95,20 +99,27 @@ class ApiWrapper | |
| 95 99 | 
             
              # @param type
         | 
| 96 100 | 
             
              # @param price
         | 
| 97 101 | 
             
              # @param quantity
         | 
| 102 | 
            +
              # rubocop:disable Metrics/AbcSize
         | 
| 98 103 | 
             
              def place_order(type, price, quantity)
         | 
| 99 104 | 
             
                order = send_order(type, price, quantity)
         | 
| 100 105 | 
             
                return order unless order.nil? || order.id.nil?
         | 
| 101 106 |  | 
| 102 | 
            -
                BitexBot::Robot.log(:debug, "Captured error when placing order on #{ | 
| 107 | 
            +
                BitexBot::Robot.log(:debug, "Captured error when placing order on #{name}")
         | 
| 103 108 | 
             
                # Order may have gone through and be stuck somewhere in Wrapper's pipeline.
         | 
| 104 109 | 
             
                # We just sleep for a bit and then look for the order.
         | 
| 105 | 
            -
                 | 
| 106 | 
            -
                  BitexBot::Robot. | 
| 110 | 
            +
                5.times do |i|
         | 
| 111 | 
            +
                  BitexBot::Robot.log(
         | 
| 112 | 
            +
                    :info,
         | 
| 113 | 
            +
                    "#{name} cauldn't place #{type} order #{i} times for #{base.upcase} #{quantity} @ #{quote.upcase} #{price}.\n"\
         | 
| 114 | 
            +
                    "Going to sleep 10 seconds.\n"
         | 
| 115 | 
            +
                  )
         | 
| 116 | 
            +
                  BitexBot::Robot.sleep_for(15)
         | 
| 107 117 | 
             
                  order = find_lost(type, price, quantity)
         | 
| 108 118 | 
             
                  return order if order.present?
         | 
| 109 119 | 
             
                end
         | 
| 110 | 
            -
                raise OrderNotFound, "Closing: #{type} order not found for #{ | 
| 120 | 
            +
                raise OrderNotFound, "Closing: #{type} order not found for #{base.upcase} #{quantity} @ #{quote.upcase} #{price}."
         | 
| 111 121 | 
             
              end
         | 
| 122 | 
            +
              # rubocop:enable Metrics/AbcSize
         | 
| 112 123 |  | 
| 113 124 | 
             
              # Hook Method - arguments could not be used in their entirety by the subclasses
         | 
| 114 125 | 
             
              def send_order(_type, _price, _quantity)
         | 
| @@ -67,7 +67,8 @@ class BitexApiWrapper < ApiWrapper | |
| 67 67 | 
             
              end
         | 
| 68 68 |  | 
| 69 69 | 
             
              def send_order(type, price, quantity, wait = false)
         | 
| 70 | 
            -
                { sell: Bitex::Ask, buy: Bitex::Bid }[type].create!(base_quote.to_sym, quantity, price, wait)
         | 
| 70 | 
            +
                order = { sell: Bitex::Ask, buy: Bitex::Bid }[type].create!(base_quote.to_sym, quantity, price, wait)
         | 
| 71 | 
            +
                order_parser(order) if order.present?
         | 
| 71 72 | 
             
              end
         | 
| 72 73 |  | 
| 73 74 | 
             
              def transactions
         | 
| @@ -44,11 +44,19 @@ class ItbitApiWrapper < ApiWrapper | |
| 44 44 | 
             
              end
         | 
| 45 45 |  | 
| 46 46 | 
             
              def orders
         | 
| 47 | 
            -
                Itbit::Order.all(instrument: currency_pair, status: :open).map { |o| order_parser(o) }
         | 
| 47 | 
            +
                Itbit::Order.all(instrument: currency_pair[:name], status: :open).map { |o| order_parser(o) }
         | 
| 48 48 | 
             
              end
         | 
| 49 49 |  | 
| 50 | 
            -
              def  | 
| 51 | 
            -
                Itbit::Order.create!( | 
| 50 | 
            +
              def send_order(type, price, quantity)
         | 
| 51 | 
            +
                order = Itbit::Order.create!(
         | 
| 52 | 
            +
                  type,
         | 
| 53 | 
            +
                  currency_pair[:name],
         | 
| 54 | 
            +
                  quantity.round(4),
         | 
| 55 | 
            +
                  price.round(2),
         | 
| 56 | 
            +
                  wait: true,
         | 
| 57 | 
            +
                  currency: currency_pair[:base]
         | 
| 58 | 
            +
                )
         | 
| 59 | 
            +
                order_parser(order) if order.present?
         | 
| 52 60 | 
             
              rescue RestClient::RequestTimeout => e
         | 
| 53 61 | 
             
                # On timeout errors, we still look for the latest active closing order that may be available.
         | 
| 54 62 | 
             
                # We have a magic threshold of 5 minutes and also use the price to recognize an order as the current one.
         | 
| @@ -32,7 +32,8 @@ class KrakenApiWrapper < ApiWrapper | |
| 32 32 | 
             
              end
         | 
| 33 33 |  | 
| 34 34 | 
             
              def find_lost(type, price, quantity)
         | 
| 35 | 
            -
                KrakenOrder.find_lost(type, price, quantity)
         | 
| 35 | 
            +
                order = KrakenOrder.find_lost(type, price, quantity)
         | 
| 36 | 
            +
                order_parser(order) if order.present?
         | 
| 36 37 | 
             
              end
         | 
| 37 38 |  | 
| 38 39 | 
             
              def order_book
         | 
| @@ -42,11 +43,12 @@ class KrakenApiWrapper < ApiWrapper | |
| 42 43 | 
             
              end
         | 
| 43 44 |  | 
| 44 45 | 
             
              def orders
         | 
| 45 | 
            -
                KrakenOrder.open.map { | | 
| 46 | 
            +
                KrakenOrder.open.map { |o| order_parser(o) }
         | 
| 46 47 | 
             
              end
         | 
| 47 48 |  | 
| 48 49 | 
             
              def send_order(type, price, quantity)
         | 
| 49 | 
            -
                KrakenOrder.create!(type, price, quantity)
         | 
| 50 | 
            +
                order = KrakenOrder.create!(type, price, quantity)
         | 
| 51 | 
            +
                order_parser(order) if order.present?
         | 
| 50 52 | 
             
              end
         | 
| 51 53 |  | 
| 52 54 | 
             
              def transactions
         | 
| @@ -65,8 +67,8 @@ class KrakenApiWrapper < ApiWrapper | |
| 65 67 | 
             
              def balance_summary_parser(balances)
         | 
| 66 68 | 
             
                open_orders = KrakenOrder.open
         | 
| 67 69 | 
             
                BalanceSummary.new(
         | 
| 68 | 
            -
                  balance_parser(balances, currency_pair[:base],  | 
| 69 | 
            -
                  balance_parser(balances, currency_pair[:quote],  | 
| 70 | 
            +
                  balance_parser(balances, currency_pair[:base], crypto_reserved(open_orders)),
         | 
| 71 | 
            +
                  balance_parser(balances, currency_pair[:quote], fiat_reserved(open_orders)),
         | 
| 70 72 | 
             
                  client.private.trade_volume(pair: currency_pair[:altname])[:fees][currency_pair[:name]][:fee].to_d
         | 
| 71 73 | 
             
                )
         | 
| 72 74 | 
             
              end
         | 
| @@ -76,12 +78,12 @@ class KrakenApiWrapper < ApiWrapper | |
| 76 78 | 
             
                Balance.new(balances[currency].to_d, reserved, balances[currency].to_d - reserved)
         | 
| 77 79 | 
             
              end
         | 
| 78 80 |  | 
| 79 | 
            -
              def  | 
| 80 | 
            -
                orders_by(open_orders, :sell). | 
| 81 | 
            +
              def crypto_reserved(open_orders)
         | 
| 82 | 
            +
                orders_by(open_orders, :sell).sum { |o| (o.amount - o.executed_amount).to_d }
         | 
| 81 83 | 
             
              end
         | 
| 82 84 |  | 
| 83 | 
            -
              def  | 
| 84 | 
            -
                orders_by(open_orders, :buy). | 
| 85 | 
            +
              def fiat_reserved(open_orders)
         | 
| 86 | 
            +
                orders_by(open_orders, :buy).sum { |o| (o.amount - o.executed_amount) * o.price.to_d }
         | 
| 85 87 | 
             
              end
         | 
| 86 88 |  | 
| 87 89 | 
             
              def orders_by(open_orders, order_type)
         | 
| @@ -100,9 +102,10 @@ class KrakenApiWrapper < ApiWrapper | |
| 100 102 | 
             
                stock_market.map { |stock| OrderSummary.new(stock[0].to_d, stock[1].to_d) }
         | 
| 101 103 | 
             
              end
         | 
| 102 104 |  | 
| 103 | 
            -
              # <KrakenOrder: @id= | 
| 105 | 
            +
              # <KrakenOrder:0x007faf255382d0 @id="OGZ3HI-5I322-OIOV52", @type=:sell, @datetime=1546971756, @amount=0.248752e-2,
         | 
| 106 | 
            +
              #  @executed_amount=0.248752e-2, @price=0.40025e4, @avg_price=0.40074e4>
         | 
| 104 107 | 
             
              def order_parser(order)
         | 
| 105 | 
            -
                Order.new(order.id | 
| 108 | 
            +
                Order.new(order.id, order.type, order.price, order.amount, order.datetime, order)
         | 
| 106 109 | 
             
              end
         | 
| 107 110 |  | 
| 108 111 | 
             
              # [
         | 
| @@ -8,7 +8,9 @@ class KrakenOrder | |
| 8 8 | 
             
              # rubocop:disable Metrics/AbcSize
         | 
| 9 9 | 
             
              def self.create!(type, price, quantity)
         | 
| 10 10 | 
             
                self.last_closed_order = closed.first.try(:id) || Time.now.to_i
         | 
| 11 | 
            -
                 | 
| 11 | 
            +
                order = place_order(type, price.truncate(1), quantity.truncate(8))
         | 
| 12 | 
            +
                order_id = order['txid'].first
         | 
| 13 | 
            +
                find(order_id)
         | 
| 12 14 | 
             
              rescue KrakenClient::ErrorResponse => e
         | 
| 13 15 | 
             
                # Order could not be placed
         | 
| 14 16 | 
             
                if e.message == 'EService:Unavailable'
         | 
| @@ -23,9 +25,11 @@ class KrakenOrder | |
| 23 25 | 
             
              end
         | 
| 24 26 | 
             
              # rubocop:enable Metrics/AbcSize
         | 
| 25 27 |  | 
| 26 | 
            -
               | 
| 28 | 
            +
              # <KrakenOrder:0x007faf255382d0 @id="OGZ3HI-5I322-OIOV52", @type=:sell, @datetime=1546971756, @amount=0.248752e-2,
         | 
| 29 | 
            +
              #  @executed_amount=0.248752e-2, @price=0.40025e4, @avg_price=0.40074e4>
         | 
| 30 | 
            +
              def self.place_order(type, price, quantity)
         | 
| 27 31 | 
             
                api_wrapper.client.private.add_order(
         | 
| 28 | 
            -
                  pair:  | 
| 32 | 
            +
                  pair: api_wrapper.currency_pair[:altname],
         | 
| 29 33 | 
             
                  type: type,
         | 
| 30 34 | 
             
                  ordertype: 'limit',
         | 
| 31 35 | 
             
                  price: price,
         | 
| @@ -39,6 +43,7 @@ class KrakenOrder | |
| 39 43 | 
             
                retry
         | 
| 40 44 | 
             
              end
         | 
| 41 45 |  | 
| 46 | 
            +
              # [BigDecimal, BigDecimal]
         | 
| 42 47 | 
             
              def self.amount_and_quantity(order_id)
         | 
| 43 48 | 
             
                order = find(order_id)
         | 
| 44 49 | 
             
                amount = order.avg_price * order.executed_amount
         | 
| @@ -61,12 +66,12 @@ class KrakenOrder | |
| 61 66 |  | 
| 62 67 | 
             
              def self.find_lost(type, price, quantity)
         | 
| 63 68 | 
             
                BitexBot::Robot.log(:debug, "Looking for #{type} order in open orders...")
         | 
| 64 | 
            -
                order = open_order_by(type, price, quantity)
         | 
| 69 | 
            +
                order = open_order_by(type, price.truncate(2), quantity.truncate(8))
         | 
| 65 70 | 
             
                return log_and_return(order, :open) if order.present?
         | 
| 66 71 |  | 
| 67 72 | 
             
                BitexBot::Robot.log(:debug, "Looking for #{type} order in closed orders...")
         | 
| 68 | 
            -
                order = closed_order_by(type, price, quantity)
         | 
| 69 | 
            -
                return log_and_return(order, :closed) if order && order.id != last_closed_order
         | 
| 73 | 
            +
                order = closed_order_by(type, price.truncate(2), quantity.truncate(8))
         | 
| 74 | 
            +
                return log_and_return(order, :closed) if order.present? && order.id != last_closed_order
         | 
| 70 75 | 
             
              end
         | 
| 71 76 |  | 
| 72 77 | 
             
              def self.log_and_return(order, status)
         | 
| @@ -76,12 +81,12 @@ class KrakenOrder | |
| 76 81 |  | 
| 77 82 | 
             
              # description: [type, price, quantity]
         | 
| 78 83 | 
             
              def self.open_order_by(type, price, quantity)
         | 
| 79 | 
            -
                open.detect { | | 
| 84 | 
            +
                open.detect { |order| order == [type, price, quantity] }
         | 
| 80 85 | 
             
              end
         | 
| 81 86 |  | 
| 82 87 | 
             
              # description: [type, price, quantity]
         | 
| 83 88 | 
             
              def self.closed_order_by(type, price, quantity)
         | 
| 84 | 
            -
                closed(start: last_closed_order).detect { | | 
| 89 | 
            +
                closed(start: last_closed_order).detect { |order| order == [type, price, quantity] }
         | 
| 85 90 | 
             
              end
         | 
| 86 91 |  | 
| 87 92 | 
             
              # id: 'O5TDV2-WDYB2-6OGJRD'
         | 
| @@ -102,5 +102,13 @@ module BitexBot | |
| 102 102 | 
             
                def self.taker_specie_to_obtain
         | 
| 103 103 | 
             
                  Robot.taker.quote.upcase
         | 
| 104 104 | 
             
                end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                def self.maker_balance
         | 
| 107 | 
            +
                  store.maker_fiat
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                def self.available_maker_balance
         | 
| 111 | 
            +
                  Robot.maker.balance.fiat.available
         | 
| 112 | 
            +
                end
         | 
| 105 113 | 
             
              end
         | 
| 106 114 | 
             
            end
         | 
| @@ -8,6 +8,7 @@ module BitexBot | |
| 8 8 | 
             
                cattr_reader(:close_time_to_live) { 30 }
         | 
| 9 9 |  | 
| 10 10 | 
             
                # Start a new CloseBuy that closes existing OpenBuy's by selling on another exchange what was just bought on bitex.
         | 
| 11 | 
            +
                # rubocop:disable Metrics/AbcSize
         | 
| 11 12 | 
             
                def self.close_open_positions
         | 
| 12 13 | 
             
                  return unless open_positions.any?
         | 
| 13 14 |  | 
| @@ -15,11 +16,18 @@ module BitexBot | |
| 15 16 | 
             
                  quantity = positions.sum(&:quantity)
         | 
| 16 17 | 
             
                  amount = positions.sum(&:amount) / fx_rate
         | 
| 17 18 | 
             
                  price = suggested_amount(positions) / quantity
         | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 19 | 
            +
                  unless Robot.taker.enough_order_size?(quantity, price)
         | 
| 20 | 
            +
                    Robot.log(
         | 
| 21 | 
            +
                      :info,
         | 
| 22 | 
            +
                      "Closing: #{Robot.taker.name} - enough order size for #{Robot.taker.base.upcase} #{quantity}"\
         | 
| 23 | 
            +
                      " @ #{Robot.taker.quote.upcase} #{price}"
         | 
| 24 | 
            +
                    )
         | 
| 25 | 
            +
                    return
         | 
| 26 | 
            +
                  end
         | 
| 20 27 |  | 
| 21 28 | 
             
                  create_closing_flow!(price, quantity, amount, positions)
         | 
| 22 29 | 
             
                end
         | 
| 30 | 
            +
                # rubocop:enable Metrics/AbcSize
         | 
| 23 31 |  | 
| 24 32 | 
             
                def self.open_positions
         | 
| 25 33 | 
             
                  open_position_class.open
         | 
| @@ -31,8 +39,13 @@ module BitexBot | |
| 31 39 | 
             
                end
         | 
| 32 40 |  | 
| 33 41 | 
             
                def self.create_closing_flow!(price, quantity, amount, open_positions)
         | 
| 34 | 
            -
                  create!(desired_price: price, quantity: quantity, amount: amount, open_positions: open_positions)
         | 
| 35 | 
            -
             | 
| 42 | 
            +
                  flow = create!(desired_price: price, quantity: quantity, amount: amount, open_positions: open_positions)
         | 
| 43 | 
            +
                  Robot.log(
         | 
| 44 | 
            +
                    :info,
         | 
| 45 | 
            +
                    "Closing: created #{self}##{flow.id}, desired price: #{flow.desired_price}, quantity: #{flow.quantity}, "\
         | 
| 46 | 
            +
                    "amount: #{flow.amount}."
         | 
| 47 | 
            +
                  )
         | 
| 48 | 
            +
                  flow.create_initial_order_and_close_position!
         | 
| 36 49 | 
             
                  nil
         | 
| 37 50 | 
             
                end
         | 
| 38 51 | 
             
                # end: close_open_positions helpers
         | 
| @@ -105,7 +118,7 @@ module BitexBot | |
| 105 118 | 
             
                    Robot.log(
         | 
| 106 119 | 
             
                      :info,
         | 
| 107 120 | 
             
                      "Closing: Finished #{self.class} ##{id} earned"\
         | 
| 108 | 
            -
                      "#{Robot.maker.quote.upcase} #{fiat_profit} and #{Robot.maker.base.upcase} #{crypto_profit}."
         | 
| 121 | 
            +
                      " #{Robot.maker.quote.upcase} #{fiat_profit} and #{Robot.maker.base.upcase} #{crypto_profit}."
         | 
| 109 122 | 
             
                    )
         | 
| 110 123 | 
             
                  end
         | 
| 111 124 | 
             
                end
         | 
| @@ -126,6 +139,7 @@ module BitexBot | |
| 126 139 |  | 
| 127 140 | 
             
                # This use hooks methods, these must be defined in the subclass:
         | 
| 128 141 | 
             
                #   order_type
         | 
| 142 | 
            +
                # rubocop:disable Metrics/AbcSize
         | 
| 129 143 | 
             
                def create_order_and_close_position(quantity, price)
         | 
| 130 144 | 
             
                  # TODO: investigate how to generate an ID to insert in the fields of goals where possible.
         | 
| 131 145 | 
             
                  Robot.log(
         | 
| @@ -134,7 +148,14 @@ module BitexBot | |
| 134 148 | 
             
                    " #{Robot.taker.base.upcase} #{quantity} @ #{Robot.taker.quote.upcase} #{price}"
         | 
| 135 149 | 
             
                  )
         | 
| 136 150 | 
             
                  order = Robot.taker.place_order(order_type, price, quantity)
         | 
| 151 | 
            +
                  Robot.log(
         | 
| 152 | 
            +
                    :info,
         | 
| 153 | 
            +
                    "Closing: #{Robot.taker.name} placed #{order.type} with price: #{order.price} @ quantity #{order.amount}.\n"\
         | 
| 154 | 
            +
                    "Closing: Going to create Close#{order.type.to_s.capitalize} position.\n"
         | 
| 155 | 
            +
                  )
         | 
| 156 | 
            +
             | 
| 137 157 | 
             
                  close_positions.create!(order_id: order.id)
         | 
| 138 158 | 
             
                end
         | 
| 159 | 
            +
                # rubocop:enable Metrics/AbcSize
         | 
| 139 160 | 
             
              end
         | 
| 140 161 | 
             
            end
         | 
| @@ -35,6 +35,12 @@ module BitexBot | |
| 35 35 | 
             
                  self.store = store
         | 
| 36 36 |  | 
| 37 37 | 
             
                  remote_value, safest_price = calc_remote_value(maker_fee, taker_fee, taker_orders, taker_transactions)
         | 
| 38 | 
            +
                  Robot.log(
         | 
| 39 | 
            +
                    :info,
         | 
| 40 | 
            +
                    "Opening: Need #{taker_specie_to_spend} #{remote_value.truncate(8)} on #{Robot.taker.name} taker,"\
         | 
| 41 | 
            +
                    " has #{taker_balance.truncate(8)}."
         | 
| 42 | 
            +
                  )
         | 
| 43 | 
            +
             | 
| 38 44 | 
             
                  unless enough_remote_funds?(taker_balance, remote_value)
         | 
| 39 45 | 
             
                    raise CannotCreateFlow,
         | 
| 40 46 | 
             
                          "Needed #{remote_value} but you only have #{taker_specie_to_spend} #{taker_balance} on your taker market."
         | 
| @@ -45,22 +51,26 @@ module BitexBot | |
| 45 51 | 
             
                  order = create_order!(price)
         | 
| 46 52 | 
             
                  unless enough_funds?(order)
         | 
| 47 53 | 
             
                    raise CannotCreateFlow,
         | 
| 48 | 
            -
                          " | 
| 54 | 
            +
                          "Needed #{maker_specie_to_spend} #{value_per_order} on #{Robot.maker.name} maker to place this #{order_class}"\
         | 
| 55 | 
            +
                          " but you only have #{maker_specie_to_spend} #{available_maker_balance}."
         | 
| 49 56 | 
             
                  end
         | 
| 50 57 |  | 
| 51 | 
            -
                   | 
| 52 | 
            -
                    :info,
         | 
| 53 | 
            -
                    "Opening: Placed #{order_class} ##{order.id} #{value_per_order} @ #{Robot.maker.quote.upcase} #{price}"\
         | 
| 54 | 
            -
                    " (#{maker_specie_to_obtain} #{remote_value})"
         | 
| 55 | 
            -
                  )
         | 
| 56 | 
            -
             | 
| 57 | 
            -
                  create!(
         | 
| 58 | 
            +
                  flow = create!(
         | 
| 58 59 | 
             
                    price: price,
         | 
| 59 60 | 
             
                    value_to_use: value_to_use,
         | 
| 60 61 | 
             
                    suggested_closing_price: safest_price,
         | 
| 61 62 | 
             
                    status: 'executing',
         | 
| 62 63 | 
             
                    order_id: order.id
         | 
| 63 64 | 
             
                  )
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                  Robot.log(
         | 
| 67 | 
            +
                    :info,
         | 
| 68 | 
            +
                    "Opening: Placed #{order_class} ##{order.id} #{maker_specie_to_spend} #{value_per_order} @ #{price.truncate(2)}."\
         | 
| 69 | 
            +
                    " (#{maker_specie_to_obtain} #{remote_value})."\
         | 
| 70 | 
            +
                    " #{name.demodulize}##{flow.id} suggests closing price #{Robot.taker.quote.upcase}"\
         | 
| 71 | 
            +
                    " #{flow.suggested_closing_price}."
         | 
| 72 | 
            +
                  )
         | 
| 73 | 
            +
                  flow
         | 
| 64 74 | 
             
                rescue StandardError => e
         | 
| 65 75 | 
             
                  raise CannotCreateFlow, e.message
         | 
| 66 76 | 
             
                end
         | 
| @@ -115,8 +125,8 @@ module BitexBot | |
| 115 125 | 
             
                def self.create_open_position!(transaction, flow)
         | 
| 116 126 | 
             
                  Robot.log(
         | 
| 117 127 | 
             
                    :info,
         | 
| 118 | 
            -
                    "Opening: #{ | 
| 119 | 
            -
                    " @ #{Robot.maker.quote.upcase} #{transaction.price}"
         | 
| 128 | 
            +
                    "Opening: #{self} ##{flow.id} was hit for #{Robot.maker.base.upcase} #{transaction.raw.quantity}"\
         | 
| 129 | 
            +
                    " @ #{Robot.maker.quote.upcase} #{transaction.price}. Creating #{open_position_class}..."
         | 
| 120 130 | 
             
                  )
         | 
| 121 131 |  | 
| 122 132 | 
             
                  open_position_class.create!(
         | 
| @@ -102,5 +102,13 @@ module BitexBot | |
| 102 102 | 
             
                def self.taker_specie_to_obtain
         | 
| 103 103 | 
             
                  Robot.taker.base.upcase
         | 
| 104 104 | 
             
                end
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                def self.maker_balance
         | 
| 107 | 
            +
                  store.maker_crypto
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                def self.available_maker_balance
         | 
| 111 | 
            +
                  Robot.maker.balance.crypto.available
         | 
| 112 | 
            +
                end
         | 
| 105 113 | 
             
              end
         | 
| 106 114 | 
             
            end
         | 
    
        data/lib/bitex_bot/robot.rb
    CHANGED
    
    | @@ -71,7 +71,7 @@ module BitexBot | |
| 71 71 |  | 
| 72 72 | 
             
                def self.start_robot
         | 
| 73 73 | 
             
                  setup
         | 
| 74 | 
            -
                  log(:info,  | 
| 74 | 
            +
                  log(:info, "Loading trading robot, ctrl+c *once* to exit gracefully.\n")
         | 
| 75 75 | 
             
                  new
         | 
| 76 76 | 
             
                end
         | 
| 77 77 |  | 
| @@ -84,19 +84,19 @@ module BitexBot | |
| 84 84 | 
             
                  sync_closing_flows if active_closing_flows?
         | 
| 85 85 | 
             
                  start_opening_flows_if_needed
         | 
| 86 86 | 
             
                rescue CannotCreateFlow => e
         | 
| 87 | 
            -
                  notify("#{e.message} | 
| 87 | 
            +
                  notify("#{e.class} - #{e.message}\n\n#{e.backtrace.join("\n")}")
         | 
| 88 88 | 
             
                  sleep_for(60 * 3)
         | 
| 89 89 | 
             
                rescue Curl::Err::TimeoutError => e
         | 
| 90 | 
            -
                   | 
| 90 | 
            +
                  notify("#{e.class} - #{e.message}\n\n#{e.backtrace.join("\n")}")
         | 
| 91 91 | 
             
                  sleep_for(15)
         | 
| 92 92 | 
             
                rescue OrderNotFound => e
         | 
| 93 | 
            -
                  notify("#{e.class} - #{e.message} | 
| 93 | 
            +
                  notify("#{e.class} - #{e.message}\n\n#{e.backtrace.join("\n")}")
         | 
| 94 94 | 
             
                rescue ApiWrapperError => e
         | 
| 95 | 
            -
                  notify("#{e.class} - #{e.message} | 
| 95 | 
            +
                  notify("#{e.class} - #{e.message}\n\n#{e.backtrace.join("\n")}")
         | 
| 96 96 | 
             
                rescue OrderArgumentError => e
         | 
| 97 | 
            -
                  notify("#{e.class} - #{e.message} | 
| 97 | 
            +
                  notify("#{e.class} - #{e.message}\n\n#{e.backtrace.join("\n")}")
         | 
| 98 98 | 
             
                rescue StandardError => e
         | 
| 99 | 
            -
                  notify("#{e.class} - #{e.message} | 
| 99 | 
            +
                  notify("#{e.class} - #{e.message}\n\n#{e.backtrace.join("\n")}")
         | 
| 100 100 | 
             
                  sleep_for(60 * 2)
         | 
| 101 101 | 
             
                end
         | 
| 102 102 | 
             
                # rubocop:enable Metrics/AbcSize
         | 
| @@ -168,7 +168,9 @@ module BitexBot | |
| 168 168 |  | 
| 169 169 | 
             
                  maker_balance = with_cooldown { maker.balance }
         | 
| 170 170 | 
             
                  taker_balance = with_cooldown { taker.balance }
         | 
| 171 | 
            +
             | 
| 171 172 | 
             
                  sync_log_and_store(maker_balance, taker_balance)
         | 
| 173 | 
            +
                  log_balances('Store: Current balances.')
         | 
| 172 174 |  | 
| 173 175 | 
             
                  check_balance_warning if expired_last_warning?
         | 
| 174 176 | 
             
                  return if stop_opening_flows?
         | 
| @@ -188,6 +190,7 @@ module BitexBot | |
| 188 190 | 
             
                end
         | 
| 189 191 |  | 
| 190 192 | 
             
                def sync_log_and_store(maker_balance, taker_balance)
         | 
| 193 | 
            +
                  log_balances('Store: Updating log, maker and taker balances...')
         | 
| 191 194 | 
             
                  file = Settings.log.try(:file)
         | 
| 192 195 | 
             
                  last_log = `tail -c 61440 #{file}` if file.present?
         | 
| 193 196 |  | 
| @@ -198,13 +201,24 @@ module BitexBot | |
| 198 201 | 
             
                  )
         | 
| 199 202 | 
             
                end
         | 
| 200 203 |  | 
| 204 | 
            +
                # rubocop:disable Metrics/AbcSize
         | 
| 205 | 
            +
                def log_balances(header)
         | 
| 206 | 
            +
                  log(
         | 
| 207 | 
            +
                    :info,
         | 
| 208 | 
            +
                    "#{header}\n"\
         | 
| 209 | 
            +
                    "Store: #{maker.name} maker - #{maker.base.upcase}: #{store.maker_crypto}, #{maker.quote.upcase}: #{store.maker_fiat}.\n"\
         | 
| 210 | 
            +
                    "Store: #{taker.name} taker - #{taker.base.upcase}: #{store.taker_crypto}, #{taker.quote.upcase}: #{store.taker_fiat}.\n"
         | 
| 211 | 
            +
                  )
         | 
| 212 | 
            +
                end
         | 
| 213 | 
            +
                # rubocop:enable Metrics/AbcSize
         | 
| 214 | 
            +
             | 
| 201 215 | 
             
                def expired_last_warning?
         | 
| 202 216 | 
             
                  store.last_warning.nil? || store.last_warning < 30.minutes.ago
         | 
| 203 217 | 
             
                end
         | 
| 204 218 |  | 
| 205 219 | 
             
                def stop_opening_flows?
         | 
| 206 | 
            -
                  (log(: | 
| 207 | 
            -
                    (log(: | 
| 220 | 
            +
                  (log(:info, "Opening: Not placing new orders, #{maker.quote.upcase} target not met") if alert?(:fiat, :stop)) ||
         | 
| 221 | 
            +
                    (log(:info, "Opening: Not placing new orders, #{maker.base.upcase} target not met") if alert?(:crypto, :stop))
         | 
| 208 222 | 
             
                end
         | 
| 209 223 |  | 
| 210 224 | 
             
                def check_balance_warning
         | 
| @@ -229,7 +243,7 @@ module BitexBot | |
| 229 243 | 
             
                end
         | 
| 230 244 |  | 
| 231 245 | 
             
                def notify(message, subj = 'Notice from your robot trader')
         | 
| 232 | 
            -
                  log(: | 
| 246 | 
            +
                  log(:info, "Sending mail with subject: #{subj}\n\n#{message}")
         | 
| 233 247 | 
             
                  return unless Settings.mailer.present?
         | 
| 234 248 |  | 
| 235 249 | 
             
                  new_mail(subj, message).tap do |mail|
         | 
    
        data/lib/bitex_bot/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: bitex_bot
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.9. | 
| 4 | 
            +
              version: 0.9.1
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Nubis
         | 
| @@ -9,7 +9,7 @@ authors: | |
| 9 9 | 
             
            autorequire: 
         | 
| 10 10 | 
             
            bindir: bin
         | 
| 11 11 | 
             
            cert_chain: []
         | 
| 12 | 
            -
            date: 2019-01- | 
| 12 | 
            +
            date: 2019-01-14 00:00:00.000000000 Z
         | 
| 13 13 | 
             
            dependencies:
         | 
| 14 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 15 15 | 
             
              name: activerecord
         |