bitex_bot 0.3.7 → 0.4.0

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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +63 -0
  3. data/.rubocop.yml +33 -0
  4. data/Gemfile +1 -1
  5. data/Rakefile +1 -1
  6. data/bin/bitex_bot +1 -1
  7. data/bitex_bot.gemspec +34 -34
  8. data/lib/bitex_bot/database.rb +67 -67
  9. data/lib/bitex_bot/models/api_wrappers/api_wrapper.rb +142 -0
  10. data/lib/bitex_bot/models/api_wrappers/bitstamp/bitstamp_api_wrapper.rb +137 -0
  11. data/lib/bitex_bot/models/api_wrappers/itbit/itbit_api_wrapper.rb +116 -0
  12. data/lib/bitex_bot/models/api_wrappers/kraken/kraken_api_wrapper.rb +111 -0
  13. data/lib/bitex_bot/models/api_wrappers/kraken/kraken_order.rb +117 -0
  14. data/lib/bitex_bot/models/buy_closing_flow.rb +23 -16
  15. data/lib/bitex_bot/models/buy_opening_flow.rb +48 -54
  16. data/lib/bitex_bot/models/close_buy.rb +2 -2
  17. data/lib/bitex_bot/models/closing_flow.rb +98 -79
  18. data/lib/bitex_bot/models/open_buy.rb +11 -10
  19. data/lib/bitex_bot/models/open_sell.rb +11 -10
  20. data/lib/bitex_bot/models/opening_flow.rb +157 -99
  21. data/lib/bitex_bot/models/order_book_simulator.rb +62 -67
  22. data/lib/bitex_bot/models/sell_closing_flow.rb +25 -20
  23. data/lib/bitex_bot/models/sell_opening_flow.rb +47 -54
  24. data/lib/bitex_bot/models/store.rb +3 -1
  25. data/lib/bitex_bot/robot.rb +203 -176
  26. data/lib/bitex_bot/settings.rb +71 -12
  27. data/lib/bitex_bot/version.rb +1 -1
  28. data/lib/bitex_bot.rb +40 -16
  29. data/settings.rb.sample +43 -66
  30. data/spec/bitex_bot/settings_spec.rb +87 -15
  31. data/spec/factories/bitex_buy.rb +3 -3
  32. data/spec/factories/bitex_sell.rb +3 -3
  33. data/spec/factories/buy_opening_flow.rb +1 -1
  34. data/spec/factories/open_buy.rb +12 -10
  35. data/spec/factories/open_sell.rb +12 -10
  36. data/spec/factories/sell_opening_flow.rb +1 -1
  37. data/spec/models/api_wrappers/bitstamp_api_wrapper_spec.rb +200 -0
  38. data/spec/models/api_wrappers/itbit_api_wrapper_spec.rb +176 -0
  39. data/spec/models/api_wrappers/kraken_api_wrapper_spec.rb +209 -0
  40. data/spec/models/bitex_api_spec.rb +1 -1
  41. data/spec/models/buy_closing_flow_spec.rb +140 -71
  42. data/spec/models/buy_opening_flow_spec.rb +126 -56
  43. data/spec/models/order_book_simulator_spec.rb +10 -10
  44. data/spec/models/robot_spec.rb +61 -47
  45. data/spec/models/sell_closing_flow_spec.rb +130 -62
  46. data/spec/models/sell_opening_flow_spec.rb +129 -60
  47. data/spec/spec_helper.rb +19 -16
  48. data/spec/support/bitex_stubs.rb +13 -14
  49. data/spec/support/bitstamp/bitstamp_api_wrapper_stubs.rb +35 -0
  50. data/spec/support/bitstamp/bitstamp_stubs.rb +91 -0
  51. metadata +60 -42
  52. data/lib/bitex_bot/models/bitfinex_api_wrapper.rb +0 -118
  53. data/lib/bitex_bot/models/bitstamp_api_wrapper.rb +0 -82
  54. data/lib/bitex_bot/models/itbit_api_wrapper.rb +0 -68
  55. data/lib/bitex_bot/models/kraken_api_wrapper.rb +0 -188
  56. data/spec/models/bitfinex_api_wrapper_spec.rb +0 -17
  57. data/spec/models/bitstamp_api_wrapper_spec.rb +0 -15
  58. data/spec/models/itbit_api_wrapper_spec.rb +0 -15
  59. data/spec/support/bitstamp_stubs.rb +0 -110
@@ -0,0 +1,116 @@
1
+ # Wrapper implementation for Itbit API.
2
+ # https://api.itbit.com/docs
3
+ class ItbitApiWrapper < ApiWrapper
4
+ def self.setup(settings)
5
+ Itbit.tap do |conf|
6
+ conf.client_key = settings.client_key
7
+ conf.secret = settings.secret
8
+ conf.user_id = settings.user_id
9
+ conf.default_wallet_id = settings.default_wallet_id
10
+ conf.sandbox = settings.sandbox
11
+ end
12
+ end
13
+
14
+ def self.amount_and_quantity(order_id, _transactions)
15
+ order = Itbit::Order.find(order_id)
16
+ amount = order.volume_weighted_average_price * order.amount_filled
17
+ quantity = order.amount_filled
18
+
19
+ [amount, quantity]
20
+ end
21
+
22
+ def self.balance
23
+ balance_summary_parser(wallet[:balances])
24
+ end
25
+
26
+ def self.find_lost(type, price, _quantity)
27
+ orders.find { |o| o.type == type && o.price == price && o.timestamp >= 5.minutes.ago.to_i }
28
+ end
29
+
30
+ def self.order_book
31
+ order_book_parser(Itbit::XBTUSDMarketData.orders)
32
+ end
33
+
34
+ def self.orders
35
+ Itbit::Order.all(status: :open).map { |o| order_parser(o) }
36
+ end
37
+
38
+ def self.place_order(type, price, quantity)
39
+ Itbit::Order.create!(type, :xbtusd, quantity.round(4), price.round(2), wait: true)
40
+ rescue RestClient::RequestTimeout => e
41
+ # On timeout errors, we still look for the latest active closing order that may be available.
42
+ # We have a magic threshold of 5 minutes and also use the price to recognize an order as the current one.
43
+ # TODO: Maybe we can identify the order using metadata instead of price.
44
+ BitexBot::Robot.log(:error, 'Captured Timeout on itbit')
45
+ latest = last_order_by(price)
46
+ return latest if latest.present?
47
+
48
+ BitexBot::Robot.log(:error, 'Could not find my order')
49
+ raise e
50
+ end
51
+
52
+ def self.transactions
53
+ Itbit::XBTUSDMarketData.trades.map { |t| transaction_parser(t.symbolize_keys) }
54
+ end
55
+
56
+ # We don't need to fetch the list of transaction for itbit since we wont actually use them later.
57
+ def self.user_transactions
58
+ []
59
+ end
60
+
61
+ private_class_method
62
+
63
+ # [
64
+ # { total_balance: 0.2e2, currency: :usd, available_balance: 0.1e2 },
65
+ # { total_balance: 0.0, currency: :xbt, available_balance: 0.0 },
66
+ # { total_balance: 0.0, currency: :eur, available_balance: 0.0 },
67
+ # { total_balance: 0.0, currency: :sgd, available_balance: 0.0 }
68
+ # ]
69
+ def self.balance_summary_parser(balances)
70
+ BalanceSummary.new(balance_parser(balances, :xbt), balance_parser(balances, :usd), 0.5.to_d)
71
+ end
72
+
73
+ def self.wallet
74
+ Itbit::Wallet.all.find { |w| w[:id] == Itbit.default_wallet_id }
75
+ end
76
+
77
+ def self.balance_parser(balances, currency)
78
+ currency_balance = balances.find { |balance| balance[:currency] == currency }
79
+ Balance.new(
80
+ currency_balance[:total_balance].to_d,
81
+ currency_balance[:total_balance].to_d - currency_balance[:available_balance].to_d,
82
+ currency_balance[:available_balance].to_d
83
+ )
84
+ end
85
+
86
+ def self.last_order_by(price)
87
+ Itbit::Order.all.select { |o| o.price == price && (o.created_time - Time.now.to_i).abs < 500 }.first
88
+ end
89
+
90
+ # {
91
+ # bids: [[0.63921e3, 0.195e1], [0.637e3, 0.47e0], [0.63e3, 0.158e1]],
92
+ # asks: [[0.6424e3, 0.4e0], [0.6433e3, 0.95e0], [0.6443e3, 0.25e0]]
93
+ # }
94
+ def self.order_book_parser(book)
95
+ OrderBook.new(Time.now.to_i, order_summary_parser(book[:bids]), order_summary_parser(book[:asks]))
96
+ end
97
+
98
+ def self.order_summary_parser(orders)
99
+ orders.map { |order| OrderSummary.new(order[0], order[1]) }
100
+ end
101
+
102
+ # <Itbit::Order:
103
+ # @id='8fd820d3-baff-4d6f-9439-ff03d816c7ce', @wallet_id='b440efce-a83c-4873-8833-802a1022b476', @side=:buy,
104
+ # @instrument=:xbtusd, @type=:limit, @amount=0.1005e1, @display_amount=0.1005e1, @price=0.1e3,
105
+ # @volume_weighted_average_price=0.0, @amount_filled=0.0, @created_time=1415290187, @status=:open,
106
+ # @metadata={foo: 'bar'}, @client_order_identifier='o'
107
+ # >
108
+ def self.order_parser(order)
109
+ Order.new(order.id, order.side, order.price, order.amount, order.created_time, order)
110
+ end
111
+
112
+ # { tid: 601855, price: 0.41814e3, amount: 0.19e-1, date: 1460161126 }
113
+ def self.transaction_parser(transaction)
114
+ Transaction.new(transaction[:tid], transaction[:price], transaction[:amount], transaction[:date])
115
+ end
116
+ end
@@ -0,0 +1,111 @@
1
+ # Wrapper implementation for Kraken API.
2
+ # https://www.kraken.com/en-us/help/api
3
+ class KrakenApiWrapper < ApiWrapper
4
+ MIN_AMOUNT = 0.002
5
+
6
+ def self.setup(settings)
7
+ HTTParty::Basement.headers('User-Agent' => BitexBot.user_agent)
8
+ @settings = settings
9
+ end
10
+
11
+ def self.client
12
+ @client ||= KrakenClient.load(@settings)
13
+ end
14
+
15
+ def self.amount_and_quantity(order_id, _transactions)
16
+ KrakenOrder.amount_and_quantity(order_id)
17
+ end
18
+
19
+ def self.balance
20
+ balance_summary_parser(client.private.balance)
21
+ rescue KrakenClient::ErrorResponse, Net::ReadTimeout
22
+ retry
23
+ end
24
+
25
+ def self.enough_order_size?(quantity, _price)
26
+ quantity >= MIN_AMOUNT
27
+ end
28
+
29
+ def self.find_lost(type, price, quantity)
30
+ KrakenOrder.find_lost(type, price, quantity)
31
+ end
32
+
33
+ def self.order_book
34
+ order_book_parser(client.public.order_book('XBTUSD')[:XXBTZUSD])
35
+ rescue NoMethodError
36
+ retry
37
+ end
38
+
39
+ def self.orders
40
+ KrakenOrder.open.map { |ko| order_parser(ko) }
41
+ end
42
+
43
+ def self.send_order(type, price, quantity)
44
+ KrakenOrder.create!(type, price, quantity)
45
+ end
46
+
47
+ def self.transactions
48
+ client.public.trades('XBTUSD')[:XXBTZUSD].reverse.map { |t| transaction_parser(t) }
49
+ rescue NoMethodError
50
+ retry
51
+ end
52
+
53
+ # We don't need to fetch the list of transactions for Kraken
54
+ def self.user_transactions
55
+ []
56
+ end
57
+
58
+ private_class_method
59
+
60
+ # { ZEUR: '1433.0939', XXBT: '0.0000000000', 'XETH': '99.7497224800' }
61
+ def self.balance_summary_parser(balances)
62
+ open_orders = KrakenOrder.open
63
+ BalanceSummary.new(
64
+ balance_parser(balances, :XXBT, btc_reserved(open_orders)),
65
+ balance_parser(balances, :ZUSD, usd_reserved(open_orders)),
66
+ client.private.trade_volume(pair: 'XBTUSD')[:fees][:XXBTZUSD][:fee].to_d
67
+ )
68
+ end
69
+
70
+ def self.balance_parser(balances, currency, reserved)
71
+ Balance.new(balances[currency].to_d, reserved, balances[currency].to_d - reserved)
72
+ end
73
+
74
+ def self.btc_reserved(open_orders)
75
+ orders_by(open_orders, :sell).map { |o| (o.amount - o.executed_amount).to_d }.sum
76
+ end
77
+
78
+ def self.usd_reserved(open_orders)
79
+ orders_by(open_orders, :buy).map { |o| (o.amount - o.executed_amount) * o.price.to_d }.sum
80
+ end
81
+
82
+ def self.orders_by(open_orders, order_type)
83
+ open_orders.select { |o| o.type == order_type }
84
+ end
85
+
86
+ # {
87
+ # 'asks': [['204.52893', '0.010', 1440291148], ['204.78790', '0.312', 1440291132]],
88
+ # 'bids': [['204.24000', '0.100', 1440291016], ['204.23010', '0.312', 1440290699]]
89
+ # }
90
+ def self.order_book_parser(book)
91
+ OrderBook.new(Time.now.to_i, order_summary_parser(book[:bids]), order_summary_parser(book[:asks]))
92
+ end
93
+
94
+ def self.order_summary_parser(stock_market)
95
+ stock_market.map { |stock| OrderSummary.new(stock[0].to_d, stock[1].to_d) }
96
+ end
97
+
98
+ # <KrakenOrder: @id='O5TDV2-WDYB2-6OGJRD', @type=:buy, @price='1.01', @amount='1.00000000', @datetime='2013-09-26 23:15:04'>
99
+ def self.order_parser(order)
100
+ Order.new(order.id.to_s, order.type, order.price, order.amount, order.datetime, order)
101
+ end
102
+
103
+ # [
104
+ # ['price', 'amount', 'timestamp', 'buy/sell', 'market/limit', 'miscellaneous']
105
+ # ['202.51626', '0.01440000', 1440277319.1922, 'b', 'l', ''],
106
+ # ['202.54000', '0.10000000', 1440277322.8993, 'b', 'l', '']
107
+ # ]
108
+ def self.transaction_parser(transaction)
109
+ Transaction.new(transaction[2].to_i, transaction[0].to_d, transaction[1].to_d, transaction[2].to_i)
110
+ end
111
+ end
@@ -0,0 +1,117 @@
1
+ require 'kraken_client'
2
+
3
+ # Wrapper for Kraken orders.
4
+ class KrakenOrder
5
+ cattr_accessor :last_closed_order
6
+ attr_accessor :id, :amount, :executed_amount, :price, :avg_price, :type, :datetime
7
+
8
+ # rubocop:disable Metrics/AbcSize
9
+ def self.create!(type, price, quantity)
10
+ self.last_closed_order = closed.first.try(:id) || Time.now.to_i
11
+ find(order_info_by(type, price.truncate(1), quantity.truncate(8))['txid'].first)
12
+ rescue KrakenClient::ErrorResponse => e
13
+ # Order could not be placed
14
+ if e.message == 'EService:Unavailable'
15
+ BitexBot::Robot.log(:debug, 'Captured EService:Unavailable error when placing order on Kraken. Retrying...')
16
+ retry
17
+ elsif e.message.start_with?('EGeneral:Invalid')
18
+ BitexBot::Robot.log(:debug, "Captured #{e.message}: type: #{type}, price: #{price}, quantity: #{quantity}")
19
+ raise OrderArgumentError, e.message
20
+ elsif e.message != 'error'
21
+ raise
22
+ end
23
+ end
24
+ # rubocop:enable Metrics/AbcSize
25
+
26
+ def self.order_info_by(type, price, quantity)
27
+ KrakenApiWrapper.client.private.add_order(pair: 'XBTUSD', type: type, ordertype: 'limit', price: price, volume: quantity)
28
+ end
29
+
30
+ def self.find(id)
31
+ new(*KrakenApiWrapper.client.private.query_orders(txid: id).first)
32
+ rescue KrakenClient::ErrorResponse
33
+ retry
34
+ end
35
+
36
+ def self.amount_and_quantity(order_id)
37
+ order = find(order_id)
38
+ amount = order.avg_price * order.executed_amount
39
+ quantity = order.executed_amount
40
+
41
+ [amount, quantity]
42
+ end
43
+
44
+ def self.open
45
+ KrakenApiWrapper.client.private.open_orders['open'].map { |o| new(*o) }
46
+ rescue KrakenClient::ErrorResponse
47
+ retry
48
+ end
49
+
50
+ def self.closed(start: 1.hour.ago.to_i)
51
+ KrakenApiWrapper.client.private.closed_orders(start: start)[:closed].map { |o| new(*o) }
52
+ rescue KrakenClient::ErrorResponse
53
+ retry
54
+ end
55
+
56
+ def self.find_lost(type, price, quantity)
57
+ BitexBot::Robot.log(:debug, "Looking for #{type} order in open orders...")
58
+ order = open_order_by(type, price, quantity)
59
+ return log_and_return(order, :open) if order.present?
60
+
61
+ BitexBot::Robot.log(:debug, "Looking for #{type} order in closed orders...")
62
+ order = closed_order_by(type, price, quantity)
63
+ return log_and_return(order, :closed) if order && order.id != last_closed_order
64
+ end
65
+
66
+ def self.log_and_return(order, status)
67
+ BitexBot::Robot.log(:debug, "Found open #{status} with ID #{order.id}")
68
+ order
69
+ end
70
+
71
+ # description: [type, price, quantity]
72
+ def self.open_order_by(type, price, quantity)
73
+ open.detect { |o| o == [type, price, quantity] }
74
+ end
75
+
76
+ # description: [type, price, quantity]
77
+ def self.closed_order_by(type, price, quantity)
78
+ closed(start: last_closed_order).detect { |o| o == [type, price, quantity] }
79
+ end
80
+
81
+ # id: 'O5TDV2-WDYB2-6OGJRD'
82
+ # order_data: {
83
+ # 'refid': nil, 'userref': nil, 'status': 'open', 'opentm': 1440292821.4839, 'starttm': 0, 'expiretm': 0,
84
+ # 'descr': {
85
+ # 'pair': 'ETHEUR', 'type': 'buy', 'ordertype': 'limit', 'price': '1.19000', 'price2': '0', 'leverage': 'none',
86
+ # 'order': 'buy 1204.00000000 ETHEUR @ limit 1.19000'
87
+ # },
88
+ # 'vol': '1204.00000000', 'vol_exec': '0.00000000', 'cost': '0.00000', 'fee': '0.00000', 'price': '0.00000',
89
+ # 'misc': '', 'oflags': 'fciq'
90
+ # }
91
+ # }
92
+ def initialize(id, order_data)
93
+ self.id = id
94
+ self.type = order_data[:descr][:type].to_sym
95
+ self.datetime = order_data[:opentm].to_i
96
+ self.amount = order_data[:vol].to_d
97
+ self.executed_amount = order_data[:vol_exec].to_d
98
+ self.price = order_data[:descr][:price].to_d
99
+ self.avg_price = order_data[:price].to_d
100
+ end
101
+
102
+ def cancel!
103
+ KrakenApiWrapper.client.private.cancel_order(txid: id)
104
+ rescue KrakenClient::ErrorResponse => e
105
+ e.message == 'EService:Unavailable' ? retry : raise
106
+ end
107
+
108
+ def ==(other)
109
+ if other.is_a?(self.class)
110
+ other.id == id
111
+ elsif other.is_a?(Array)
112
+ other == [type, price, amount]
113
+ end
114
+ end
115
+ end
116
+
117
+ class OrderArgumentError < StandardError; end
@@ -1,34 +1,41 @@
1
1
  module BitexBot
2
+ # It sold at Bitex and needs to close (buy) in the other market.
2
3
  class BuyClosingFlow < ClosingFlow
3
4
  has_many :open_positions, class_name: 'OpenBuy', foreign_key: :closing_flow_id
4
5
  has_many :close_positions, class_name: 'CloseBuy', foreign_key: :closing_flow_id
5
- scope :active, lambda { where(done: false) }
6
+
7
+ scope :active, -> { where(done: false) }
6
8
 
7
9
  def self.open_position_class
8
10
  OpenBuy
9
11
  end
10
-
11
- def order_method
12
- :sell
13
- end
14
12
 
15
- # The amount received when selling initially, minus
16
- # the amount spent re-buying the sold coins.
17
- def get_usd_profit
18
- close_positions.sum(:amount) - open_positions.sum(:amount)
19
- end
20
-
21
- # The coins we actually bought minus the coins we were supposed
22
- # to re-buy
23
- def get_btc_profit
13
+ private
14
+
15
+ # create_or_cancel! hookers
16
+ # The coins we actually bought minus the coins we were supposed to re-buy
17
+ def estimate_btc_profit
24
18
  quantity - close_positions.sum(:quantity)
25
19
  end
26
20
 
27
- def get_next_price_and_quantity
21
+ # The amount received when selling initially, minus the amount spent re-buying the sold coins.
22
+ def estimate_fiat_profit
23
+ positions_balance_amount - open_positions.sum(:amount)
24
+ end
25
+
26
+ def next_price_and_quantity
28
27
  closes = close_positions
29
- next_price = desired_price - ((closes.count * (closes.count * 3)) / 100.0)
28
+ next_price = desired_price - price_variation(closes.count)
30
29
  next_quantity = quantity - closes.sum(:quantity)
30
+
31
31
  [next_price, next_quantity]
32
32
  end
33
+ # end: create_or_cancel! hookers
34
+
35
+ # create_order_and_close_position hookers
36
+ def order_method
37
+ :sell
38
+ end
39
+ # end: create_order_and_close_position hookers
33
40
  end
34
41
  end
@@ -1,83 +1,77 @@
1
1
  module BitexBot
2
- # A workflow for buying bitcoin in Bitex and selling on another exchange. The
3
- # BuyOpeningFlow factory function estimates how much you could sell on the other
4
- # exchange and calculates a reasonable price taking into account the remote
5
- # orderbook and the recent operated volume.
2
+ # A workflow for buying bitcoin in Bitex and selling on another exchange. The BuyOpeningFlow factory function estimates how
3
+ # much you could sell on the other exchange and calculates a reasonable price taking into account the remote order book and the
4
+ # recent operated volume.
6
5
  #
7
- # When created, a BuyOpeningFlow places a Bid on Bitex for the calculated amount and
8
- # price, when the Bid is matched on Bitex an OpenBuy is created to sell the
9
- # matched amount for a higher price on the other exchange.
6
+ # When created, a BuyOpeningFlow places a Bid on Bitex for the calculated amount and price, when the Bid is matched on Bitex an
7
+ # OpenBuy is created to sell the matched amount for a higher price on the other exchange.
10
8
  #
11
- # A BuyOpeningFlow can be cancelled at any point, which will cancel the Bitex order
12
- # and any orders on the remote exchange created from its OpenBuy's
13
- #
14
- # @attr order_id The first thing a BuyOpeningFlow does is placing a Bid on Bitex,
15
- # this is its unique id.
9
+ # A BuyOpeningFlow can be cancelled at any point, which will cancel the Bitex order and any orders on the remote exchange
10
+ # created from its OpenBuy's
11
+ #
12
+ # @attr order_id The first thing a BuyOpeningFlow does is placing a Bid on Bitex, this is its unique id.
16
13
  class BuyOpeningFlow < OpeningFlow
17
-
18
- # Start a workflow for buying bitcoin on bitex and selling on the other
19
- # exchange. The amount to be spent on bitex is retrieved from Settings, if
20
- # there is not enough USD on bitex or BTC on the other exchange then no
21
- # order will be placed and an exception will be raised instead.
22
- # The amount a BuyOpeningFlow will try to buy and the price it will try to buy at
23
- # are derived from these parameters:
24
- #
25
- # @param btc_balance [BigDecimal] amount of btc available in the other
26
- # exchange that can be sold to balance this purchase.
27
- # @param order_book [[price, quantity]] a list of lists representing a bid
28
- # order book in the other exchange.
29
- # @param transactions [Hash] a list of hashes representing
30
- # all transactions in the other exchange. Each hash contains 'date', 'tid',
31
- # 'price' and 'amount', where 'amount' is the BTC transacted.
32
- # @param bitex_fee [BigDecimal] the transaction fee to pay on bitex.
33
- # @param other_fee [BigDecimal] the transaction fee to pay on the other
34
- # exchange.
14
+ # Start a workflow for buying bitcoin on bitex and selling on the other exchange. The amount to be spent on bitex is
15
+ # retrieved from Settings, if there is not enough USD on bitex or BTC on the other exchange then no order will be placed
16
+ # and an exception will be raised instead.
17
+ #
18
+ # The amount a BuyOpeningFlow will try to buy and the price it will try to buy at are derived from these parameters:
19
+ #
20
+ # @param btc_balance [BigDecimal] amount of btc available in the other exchange that can be sold to balance this purchase.
21
+ # @param order_book [[price, quantity]] a list of lists representing a bid order book in the other exchange.
22
+ # @param transactions [Hash] a list of hashes representing all transactions in the other exchange:
23
+ # Each hash contains 'date', 'tid', 'price' and 'amount', where 'amount' is the BTC transacted.
24
+ # @param maker_fee [BigDecimal] the transaction fee to pay on maker exchange.
25
+ # @param taker_fee [BigDecimal] the transaction fee to pay on taker exchange.
35
26
  # @param store [Store] An updated config for this robot, mainly to use for profit.
36
27
  #
37
28
  # @return [BuyOpeningFlow] The newly created flow.
38
- # @raise [CannotCreateFlow] If there's any problem creating this flow, for
39
- # example when you run out of USD on bitex or out of BTC on the other
40
- # exchange.
41
- def self.create_for_market(btc_balance, order_book, transactions,
42
- bitex_fee, other_fee, store)
29
+ # @raise [CannotCreateFlow] If there's any problem creating this flow, for example when you run out of USD on bitex or out
30
+ # of BTC on the other exchange.
31
+ def self.create_for_market(btc_balance, order_book, transactions, maker_fee, taker_fee, store)
43
32
  super
44
33
  end
45
-
34
+
35
+ # sync_open_positions helpers
36
+ def self.transaction_order_id(transaction)
37
+ transaction.bid_id
38
+ end
39
+
46
40
  def self.open_position_class
47
41
  OpenBuy
48
42
  end
49
-
43
+ # end: sync_open_positions helpers
44
+
45
+ # sought_transaction helpers
50
46
  def self.transaction_class
51
47
  Bitex::Buy
52
48
  end
53
-
54
- def self.transaction_order_id(transaction)
55
- transaction.bid_id
49
+ # end: sought_transaction helpers
50
+
51
+ # create_for_market helpers
52
+ def self.maker_price(bitcoin_to_resell)
53
+ value_to_use / bitcoin_to_resell * (1 - profit / 100)
56
54
  end
57
55
 
58
56
  def self.order_class
59
57
  Bitex::Bid
60
58
  end
61
-
62
- def self.value_to_use
63
- store.buying_amount_to_spend_per_order || Settings.buying.amount_to_spend_per_order
64
- end
65
-
59
+
66
60
  def self.profit
67
61
  store.buying_profit || Settings.buying.profit
68
62
  end
69
-
70
- def self.get_safest_price(transactions, order_book, dollars_to_use)
71
- OrderBookSimulator.run(Settings.time_to_live, transactions,
72
- order_book, dollars_to_use, nil)
73
- end
74
-
75
- def self.get_remote_value_to_use(value_to_use_needed, safest_price)
63
+
64
+ def self.remote_value_to_use(value_to_use_needed, safest_price)
76
65
  value_to_use_needed / safest_price
77
66
  end
78
67
 
79
- def self.get_bitex_price(usd_to_spend, bitcoin_to_resell)
80
- (usd_to_spend / bitcoin_to_resell) * (1 - self.profit / 100.0)
68
+ def self.safest_price(transactions, order_book, dollars_to_use)
69
+ OrderBookSimulator.run(Settings.time_to_live, transactions, order_book, dollars_to_use, nil)
70
+ end
71
+
72
+ def self.value_to_use
73
+ store.buying_amount_to_spend_per_order || Settings.buying.amount_to_spend_per_order
81
74
  end
75
+ # end: create_for_market helpers
82
76
  end
83
77
  end
@@ -1,7 +1,7 @@
1
1
  module BitexBot
2
- # A CloseBuy represents an Ask on the remote exchange intended
3
- # to close one or several OpenBuy positions.
2
+ # A CloseBuy represents an Ask on the remote exchange intended to close one or several OpenBuy positions.
4
3
  # TODO: document attributes.
4
+ #
5
5
  class CloseBuy < ActiveRecord::Base
6
6
  end
7
7
  end